如何在HTML5画布中剪裁形状内部?

15

我找到了一些关于剪切弧形外部区域的示例(例如:此示例)。 我似乎无法弄清如何剪裁内部弧形。

这是我目前剪切外部区域的示例,实际上与我想要的相反:

ctx.save();

ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
ctx.clip();

ctx.beginPath();
ctx.lineWidth     = 1;
ctx.shadowBlur    = 10;
ctx.shadowOffsetX = shadowOffset;
ctx.shadowColor   = '#000000';
ctx.strokeStyle   = '#000000';
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
ctx.stroke();

ctx.restore();

1
你有你期望的代码结果的图片吗? - Mike 'Pomax' Kamermans
是的,正如@Mike'Pomax'Kamermans所说,你能更好地解释一下“剪辑内部”对你意味着什么吗?它不可能只是在画布大小的图像上绘制蓝色圆圈。 - markE
在我发布的例子中,裁剪发生在弧形之外。换句话说,弧形之外的区域正在被裁剪(移除)。我想要相反的效果,即裁剪(移除)弧形内部的区域,而弧形外部的区域仍然以正常方式呈现。 - Matt Huggins
3个回答

18

可用选项

对于不规则形状,您可以使用两种技术:

  1. 复合模式
  2. 剪辑

在我看来,更好的选择是使用实际的剪辑模式或复合模式 destination-out

正如markE在他的答案中所说,xor也是可用的,但是xor仅反转alpha像素,而不删除RGB像素。这适用于没有透明度的实心图像,但是如果您有现有的带有透明度的像素,则可能会产生相反的效果(变得可见),或者如果您稍后使用xor模式与其他内容一起绘制,则“剪辑”区域将再次显示。

剪辑

通过使用剪辑,您可以简单地使用clearRect来清除路径定义的区域。

示例:

/// save context for clipping
ctx.save();

/// create path
ctx.beginPath();
ctx.arc(x, y, radius, 0, 2 * Math.PI);
ctx.closePath();

/// set clipping mask based on shape
ctx.clip();

/// clear anything inside it
ctx.clearRect(0, 0, offset, offset);

/// remove clipping mask
ctx.restore();

剪辑在线演示

源图像:一张具有部分半透明像素和完全透明的图像,其中白色背景透过来 -

Source image

结果:

我们在其中打了一个洞,背景透过来显示出来:

clipping

复合模式:目标-出

使用复合模式目标-出将像剪辑一样清除像素:

ctx.beginPath();
ctx.arc(offset * 0.5, offset * 0.5, offset * 0.3, 0, 2 * Math.PI);
ctx.closePath();

/// set composite mode
ctx.globalCompositeOperation = 'destination-out';
ctx.fill();

/// reset composite mode to default
ctx.globalCompositeOperation = 'source-over';

复合模式在线演示:

结果:

destination out

与剪切相同,destination-out会移除像素。

复合模式:异或

在存在透明像素的情况下使用xor在此在线示例中查看):

xor mode

只有 alpha 值被反转了。由于我们没有实心像素,alpha 不会从 255 变为 0 (255 - 255),而是 255 - 实际值,这将导致使用此模式时背景未被清除。

(如果您使用相同的模式再次绘制,则“删除”的像素将被恢复,因此可以以其他方式利用它们)。


10

你可以使用你的样式中的“反向”剪辑形状进行剪辑:首先绘制屏幕轮廓,然后绘制剪辑路径 - 不要使用beginPath- 然后关闭路径并进行剪辑。
这样,你描述的形状就成为了屏幕内的“孔”
路径函数必须描述一个封闭路径。

示例在这里:http://jsfiddle.net/gamealchemist/9DuJL/40/
我们可以看到,即使有多个形状,只要它们不相交,则也可以使用该方法。

标准剪辑示例

反向剪辑示例

代码如下:

// clips the canvas with the invert of provided path function
// if pathFunction is an array, remove clips defined by all functions
function clipRevert(pathFunction) {
    ctx.beginPath();
    ctx.rect(0, 0, canvas.width, ctx.canvas.height);
    if (Array.isArray(pathFunction)) pathFunction.forEach(execute);
    else pathFunction();
    ctx.clip();
}


function execute(fn) {
    return fn();
}

使用示例:(将绘制一个使用圆形反转剪辑的图像):

function circlePath(x, y, r) {
    ctx.arc(x, y, r, 0, Math.PI * 2, true);
}

window.clipRevertExample = function () {
    ctx.save();
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    clipRevert(circlePath.bind(null, 100, 60, 40));
    ctx.drawImage(img, 0, 0);
    ctx.restore();
}

最后一句话: 实际上你可以选择任意基础形状来进行减法操作。


2
您可以使用context.globalCompositeOperation="xor"来根据任何路径或绘制从现有图像中删除内容。
这种合成模式将使用应用合成之后所做的任何绘制来“取消绘制”现有像素。
在这种情况下,现有图像将基于您的弧线进行“取消绘制”:
以下是代码和Fiddle链接:http://jsfiddle.net/m1erickson/9DuJL/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    #canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

var img=new Image();
img.onload=function(){
    ctx.save();
    ctx.drawImage(img,0,0);
    ctx.globalCompositeOperation="xor";
    ctx.beginPath();
    ctx.arc(106, 77, 74, 0, Math.PI * 2, false);
    ctx.closePath();
    ctx.fill();
    ctx.restore();
}
img.src="http://i.imgur.com/gwlPu.jpg";


}); // end $(function(){});
</script>

</head>

<body>
    <canvas id="canvas" width=200 height=158></canvas>
</body>
</html>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接