9

我目前正在将图像绘制到 HTML5 画布上并用弧线对其进行遮罩,在绘制图像之前调用 clip() 以便仅显示弧线中的部分。我怎样才能使这条弧的边缘羽化?我通过谷歌搜索知道没有简单的方法可以简单地将“羽毛”应用到用画布绘制的形状上。边缘接触弧线的图像的像素数据是什么?谢谢你的帮助。

这是我的代码的相关部分:

ctx.arc(canvas.width/2, canvas.height/2, 250, 0, 6.28, false);//draw the circle
ctx.restore();
ctx.save();
ctx.drawImage(background, 0, 0,
              background.width * scale, background.height * scale);
ctx.clip();//call the clip method so the next render is clipped in last path
ctx.drawImage(img, 0, 0,
              img.width * scale, img.height * scale);
ctx.closePath();
ctx.restore();

更新

感谢您的彻底回答和非常有用的代码/评论肯!昨晚我花了几个小时试图在我的特定用例中使用这个解决方案,但我遇到了麻烦。似乎如果我使用您描述的第二画布技术剪辑图像,我无法像使用 arc() 和 clip() 例程那样在变换上重绘它。这是我想要完成的 JS Fiddle,减去弧上的羽化,注意两个分层图像上的单击和拖动事件。

http://jsfiddle.net/g3WkN/

我尝试用您的方法替换 arc(),但我很难让它响应鼠标事件上发生的转换。

4

1 回答 1

24

2017/7 更新

由于给出了这个答案,现在在较新的浏览器中提供了一个新选项,即上下文中的filter属性。请注意,目前并非所有浏览器都支持它。

对于这样做的浏览器,我们可以减少代码并像这样删除临时画布:

var ctx = demo.getContext('2d');

ctx.fillStyle = '#f90';
ctx.fillRect(0, 0, demo.width, demo.height);

clipArc(ctx, 200, 200, 150, 40);

function clipArc(ctx, x, y, r, f) {

    ctx.globalCompositeOperation = 'destination-out';

    ctx.filter = "blur(25px)";  // "feather"
    ctx.beginPath();
    ctx.arc(x, y, r, 0, 2 * Math.PI);
    ctx.fill();

    // reset comp. mode and filter
    ctx.globalCompositeOperation = 'destination-out';
    ctx.filter = "none";
}
body {background:#07c}
<canvas id="demo" width=400 height=400></canvas>

旧答案

技术

您可以通过组合以下步骤来实现此目的:

  • 使用离屏画布
  • 使用阴影功能(秘密成分)
  • 使用复合模式

这个概念是基于让浏览器利用模糊阴影在内部制作羽毛。这比 JavaScript 中的模糊要快得多。因为我们可以为任何对象制作阴影,所以您可以制作复杂的羽毛蒙版。

离屏画布仅用于绘制阴影。我们通过将实际形状移动到画布外然后相应地偏移阴影来实现这一点。结果是在屏幕外画布上绘制阴影,而实际形状是“不可见的”。

现在我们有了形状的羽化版本,我们可以将其用作复合模式的蒙版。我们选择destination-out在绘制阴影的位置进行夹板,或destination-in反转蒙版。

例子

让我们创建一个包装函数来为我们完成所有步骤

在线演示在这里

function clipArc(ctx, x, y, r, f) { /// context, x, y, radius, feather size

    /// create off-screen temporary canvas where we draw in the shadow
    var temp = document.createElement('canvas'),
        tx = temp.getContext('2d');
      
    temp.width = ctx.canvas.width;
    temp.height = ctx.canvas.height;

    /// offset the context so shape itself is drawn outside canvas
    tx.translate(-temp.width, 0);

    /// offset the shadow to compensate, draws shadow only on canvas
    tx.shadowOffsetX = temp.width;    
    tx.shadowOffsetY = 0;

    /// black so alpha gets solid
    tx.shadowColor = '#000';

    /// "feather"
    tx.shadowBlur = f;
    
    /// draw the arc, only the shadow will be inside the context
    tx.beginPath();
    tx.arc(x, y, r, 0, 2 * Math.PI);
    tx.closePath();
    tx.fill();

    /// now punch a hole in main canvas with the blurred shadow
    ctx.save();
    ctx.globalCompositeOperation = 'destination-out';
    ctx.drawImage(temp, 0, 0);
    ctx.restore();
}

这里的所有都是它的。

用法

clipArc(context, centerX, centerY, radius, featherSize);

带有演示背景(见小提琴):

ctx.fillStyle = '#ffa';
ctx.fillRect(0, 0, demo.width, demo.height);

clipArc(ctx, 200, 200, 150, 40);

结果:

演示快照

如果要保持中心不变,只需将复合模式替换为destination-in.

倒置羽毛面具演示

演示快照反转掩码

于 2013-09-26T21:50:02.223 回答