如何为 canvas 形状设置圆形内阴影?

How to have a circular inner shadow for canvas shape?

在我的 canvas 中,我有一个随光标移动的圆形。 我想给它一点内阴影,但它不起作用。

这是代码和演示:JSFIDDLE

function writeMessage(canvas, message, x, y) {
    var context = canvas.getContext('2d');
    context.clearRect(0, 0, canvas.width, canvas.height);

    var pattern = context.createPattern(imageResized, 'no-repeat'); //Use imageResized, not imageObj.
    context.fillStyle = pattern;
    context.fill();

    context.font = '28pt Calibri';
    context.fillStyle = 'white';
    //context.fillText(message, x, y);
    context.beginPath();
    context.arc(x, y, 50, 0, 2 * Math.PI);
    context.stroke();


    context.beginPath();
    context.lineWidth = 5;
    context.shadowColor = 'black';
    context.strokeStyle = "rgba(0,0,0,1)";
    context.shadowBlur = 15;
    context.shadowOffsetX = 0;
    context.shadowOffsetY = 0;
    context.arc(x, y, 50, 0, 2 * Math.PI, false);
    context.stroke();
    context.restore();

}

我注意到,如果我添加 context.clip();,此设置将起作用,但它会使整个 canvas 崩溃。

知道要为这个圆圈添加内阴影吗?

尝试在提供框架的弧之前绘制提供阴影的弧。保存 beginPath 之前的上下文状态,并在阴影弧划过之后恢复它。您需要使阴影弧的半径小于框架弧的半径,以便阴影的外边缘被框架弧的笔划覆盖。阴影弧外的外部阴影仍被绘制,但被框架弧覆盖。

示例:

context.save();
context.beginPath();
context.lineWidth = 6;
context.shadowColor = 'black';
context.strokeStyle = "rgba(0,0,0,1)";
context.shadowBlur = 15;
context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
context.arc(x, y, 47, 0, 2 * Math.PI, false);
context.stroke();
context.restore();

context.save();
context.beginPath();
context.lineWidth = 6;
context.arc(x, y, 50, 0, 2 * Math.PI);
context.stroke();
context.restore();

您需要调整圆弧的大小、颜色和线宽以获得您想要的效果。

如果在诸如 mousemove 之类的繁忙处理程序中执行 .clip 会使浏览器崩溃,您可以 pre-build 在您的应用程序开始时使用内部阴影的圆圈一次,然后重新使用该 pre-built 圆圈对性能影响不大。

以下是如何创建一个 in-memory canvas 包含带内阴影的圆圈。是的,它使用.clip,但它只在应用程序开始时使用它一次。

var PI2=Math.PI*2;
var cut=document.createElement('canvas');
var cutCtx=cut.getContext('2d');
cut.width=100;
cut.height=100;
cutCtx.arc(50,50,50,0,Math.PI*2);
cutCtx.closePath();
cutCtx.clip();
cutCtx.shadowColor='black'
cutCtx.shadowBlur=15;
for(var i=0;i<15;i++){
    cutCtx.stroke();
}

然后在 mousemove 事件中绘制那个 pre-built 圆圈并不是很昂贵。

这是示例代码和演示:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var scrollX=$canvas.scrollLeft();
var scrollY=$canvas.scrollTop();

var PI2=Math.PI*2;
var cut=document.createElement('canvas');
var cutCtx=cut.getContext('2d');
cut.width=100;
cut.height=100;
cutCtx.arc(50,50,50,0,Math.PI*2);
cutCtx.closePath();
cutCtx.clip();
cutCtx.shadowColor='black'
cutCtx.shadowBlur=15;
for(var i=0;i<15;i++){
  cutCtx.stroke();
}

ctx.fillStyle='white';
ctx.fillRect(0,0,cw,ch);

$("#canvas").mousemove(function(e){handleMouseMove(e);});


function applyCut(mx,my){
  // hide the background image by whiteing-out the canvas
  ctx.fillRect(0,0,cw,ch);

  // use compositing to "erase" a circle under the mouse
  ctx.globalCompositeOperation='destination-out';
  ctx.beginPath();
  ctx.arc(mx,my,50,0,PI2);
  ctx.closePath();
  ctx.fill();

  // draw the pre-built circle shadow under the mouse
  ctx.globalCompositeOperation='source-over';
  // Hint: the in-memory canvas can be an image source for
  // your on-screen canvas
  ctx.drawImage(cut,mx-50,my-50);
}


function handleMouseMove(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);

  applyCut(mouseX,mouseY);

}
body{ background-color: ivory; }
#wrapper{position:relative;}
#bk,#canvas{position:absolute;}
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Move the mouse to reveal the image underneath</h4>
<div id=wrapper>
  <img id=bk src=https://dl.dropboxusercontent.com/u/139992952/Whosebug/tiger.png />
  <canvas id="canvas" width=512 height=512></canvas>
</div>

我刚刚针对这种情况创建了 Inset.js!

Inset.js

只需要设置ctx.shadowInset = true;

例如:http://codepen.io/patlillis/pen/RpEoKE

var ctx = canvas.getContext("2d");
var w = canvas.width = window.innerWidth;
var h = canvas.height = window.innerHeight;

// Set up circle styles.
ctx.shadowInset = true;
ctx.shadowBlur = 15;
ctx.shadowColor = 'black';
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
ctx.fillStyle = 'red';

// Set up mouse listener.
document.addEventListener('mousemove', function(e) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.beginPath();
    ctx.arc(e.clientX, e.clientY, 30, 0, 2 * Math.PI);
    ctx.fill();
});