如何在 HTML5 画布中剪辑 INSIDE 形状?

Posted

技术标签:

【中文标题】如何在 HTML5 画布中剪辑 INSIDE 形状?【英文标题】:How can I clip INSIDE a shape in HTML5 canvas? 【发布时间】:2013-09-30 01:16:50 【问题描述】:

我发现了许多用于裁剪弧的外部区域的示例(例如:this example)。我似乎无法弄清楚如何将弧形夹在中。

这是我当前如何裁剪外部区域的示例,这与我想要的相反:

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

【问题讨论】:

你有一张你想要的代码应该产生什么结果的图片吗? 是的,正如@Mike'Pomax'Kamermans 所说,您能否更好地解释一下“内部剪辑”对您意味着什么?它不能像绘制画布大小的图像然后在该图像上绘制蓝色圆圈那么简单。 在我发布的示例中,剪切发生在弧外。换句话说,弧线之外的区域被剪裁(移除)。我想要与此相反,弧内的区域被剪裁(删除),而弧外的区域仍然正常渲染。 【参考方案1】:

可用选项

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

    复合模式 剪辑

恕我直言,最好选择使用实际剪辑模式或合成模式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();

ONLINE DEMO FOR CLIPPING

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

结果:

我们在里面打了一个洞,背景就透出来了:

复合模式:destination-out

使用复合模式destination-out 将像剪切一样清除像素:

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';

ONLINE DEMO FOR COMPOSITE MODE

结果:

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

复合模式:异或

在存在透明像素的情况下使用xor(see online example here):

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

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

【讨论】:

【参考方案2】:

您当然可以使用您的风格的“倒置”剪辑形状进行剪辑:只需先绘制 屏幕轮廓,然后在不使用 beginPath 的情况下绘制您的剪切路径,然后关闭路径并进行剪切。 这样,您描述的形状就是屏幕上的一个“洞”。 path 函数必须描述一个封闭的路径。

这里有一个演示: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();

最后一句话:实际上你可以选择一个任意的基础形状来减去。

【讨论】:

【参考方案3】:

您可以使用 context.globalCompositeOperation="xor" 根据任何路径或绘图从现有图像中删除。

此合成模式将使用您在应用合成后所做的任何绘图“取消绘制”现有像素。

在这种情况下,现有图像将根据您的弧线“未绘制”:

这是代码和小提琴: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; 
    #canvasborder: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>

【讨论】:

以上是关于如何在 HTML5 画布中剪辑 INSIDE 形状?的主要内容,如果未能解决你的问题,请参考以下文章

在 HTML5 画布中剪切多个图像

如何在 Flash html5 画布中更改影片剪辑的宽度?

如何创建中间有孔的画布 html5 剪辑区域?

如何在 Adob​​e Animate CC 2019 中将主时间线影片剪辑与 html5 画布混合

如何在 html 5 画布中删除区域的剪辑

如何在 html5 画布中绘制椭圆?