画布剪裁具有“羽毛”边缘效果

时间:2013-09-26 20:49:54

标签: javascript canvas html5-canvas

我正在将图像绘制到HTML5 Canvas并用弧屏蔽它,在绘制图像之前调用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小提琴,减去弧上的羽化,注意两个分层图像上的点击和拖动事件。

http://jsfiddle.net/g3WkN/

我尝试用你的方法替换arc(),但是我很难做到对鼠标事件发生的变换做出响应。

1 个答案:

答案 0 :(得分: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来反转遮罩。

实施例

让我们创建一个包装函数,为我们执行所有步骤

<强> ONLINE DEMO HERE

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();
}

这就是它的全部内容。

<强> USAGE

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

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

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

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

<强>结果:

Demo snapshot

如果您想保持中心完整,只需将复合模式替换为destination-in

<强> Demo for inverted feathered mask

demo snapshot inverted mask