从 HTML5 Canvas 中清除圆形区域

IT技术 javascript html canvas html5-canvas
2021-03-22 20:09:14

似乎从画布中清除区域的唯一方法是使用 clearRect() 命令 - 我需要清除一个圆圈(我正在从填充的画布中屏蔽区域,在这种特定情况下为点光源)并且尽管进行了所有尝试似乎不可能。

我尝试绘制一个 alpha 值为 0 的圆,但除非alpha 更高(这与要点相反:P),否则不会出现任何内容 - 我假设是因为 contex.fill() 将其绘制为添加而不是替换.

关于我如何能够(快速)清除用于蒙版目的的圆圈的任何建议?

6个回答

使用.arc创建一个圆形行程,然后使用.clip(),使当前裁剪区域。

然后你可以使用.clearRect()擦除整个画布,但只有剪裁的区域会改变。

请注意,许多移动浏览器仅支持矩形剪切区域。
2021-05-11 20:09:14
我永远不会自己管理 - 感谢好的解决方案!也用于清除各种奇怪的形状!
2021-05-14 20:09:14

如果您正在制作一款游戏或其他需要充分发挥性能的东西,请看看我是如何回答这个问题的:画布 - 在所有完全透明的区域中填充一个矩形

具体来说,导致这个答案的编辑:http : //jsfiddle.net/a2Age/2/

这里的巨大优势:

  • 不使用路径(慢)
  • 不使用剪辑(慢)
  • 不需要保存/恢复(因为没有清除所有状态(1)就无法重置剪辑区域,这意味着您也必须使用保存/恢复)

(1) 我实际上对此有所抱怨,因此resetClip() 已被放入官方规范中,但浏览器实现它还需要一段时间。

代码

var ctx          = document.getElementById('canvas1').getContext('2d'),
    ambientLight = 0.1,
    intensity    = 1,
    radius       = 100,
    amb          = 'rgba(0,0,0,' + (1 - ambientLight) + ')';

addLight(ctx, intensity, amb, 200, 200, 0, 200, 200, radius); // First circle
addLight(ctx, intensity, amb, 250, 270, 0, 250, 270, radius); // Second circle
addLight(ctx, intensity, amb, 50, 370, 0, 50, 370, radius, 50); // Third!

ctx.fillStyle = amb;
ctx.globalCompositeOperation = 'xor';
ctx.fillRect(0, 0, 500, 500);

function addLight(ctx, intsy, amb, xStart, yStart, rStart, xEnd, yEnd, rEnd, xOff, yOff) {
  xOff = xOff || 0;
  yOff = yOff || 0;

  var g = ctx.createRadialGradient(xStart, yStart, rStart, xEnd, yEnd, rEnd);
  g.addColorStop(1, 'rgba(0,0,0,' + (1 - intsy) + ')');
  g.addColorStop(0, amb);
  ctx.fillStyle = g;
  ctx.fillRect(xStart - rEnd + xOff, yStart - rEnd + yOff, xEnd + rEnd, yEnd + rEnd);
}
canvas {
  border: 1px solid black;
  background-image: url('http://placekitten.com/500/500');
}
<canvas id="canvas1" width="500" height="500"></canvas>

另外一个巨大的好处是你可以做径向渐变!
2021-04-28 20:09:14
添加 resetClip() 的工作做得很好!
2021-05-16 20:09:14
嘿,那里有一些漂亮的代码!我没有选择这是我的答案,因为上一个答案直接回答了我提出的问题(因此对于出于其他原因搜索相同内容的人更有用)但是您的链接可以直接应用于我正在做的事情,因为可能更好大大地!我会投票,但我显然没有足够的堆栈代表 xD 所以我衷心感谢将不得不这样做:)
2021-05-17 20:09:14

鉴于要求,这些答案很好。但是假设您和我一样,并且有其他要求:

  1. 您想要“清除”可能部分超出您正在清除的形状边界的形状的一部分。
  2. 您希望看到形状下方的背景,而不是清除背景。

对于第一个需求,解决方案是使用context.globalCompositeOperation = 'destination-out' 蓝色是第一个形状,红色是第二个形状。如您所见,destination-out从第一个形状中删除该部分。

在此处输入图片说明

下面是一些示例代码:

  explosionCanvasCtx.fillStyle = "red"
  drawCircle(explosionCanvasCtx, projectile.radius, projectile.radius, projectile.radius)
  explosionCanvasCtx.fill()

  explosionCanvasCtx.globalCompositeOperation = 'destination-out' #see https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
  drawCircle(explosionCanvasCtx, projectile.radius + 20, projectile.radius, projectile.radius)
  explosionCanvasCtx.fill()

这是潜在的问题:第二个fill()将清除它下面的所有内容,包括背景。有时您只想清除第一个形状,但仍想查看其下方的图层。

解决方案是drawImage在临时画布上绘制它,然后将临时画布绘制到主画布上。代码如下所示:

  diameter = projectile.radius * 2
  console.log "<canvas width='" + diameter + "' height='" + diameter + "'></canvas>"
  explosionCanvas = $("<canvas width='" + diameter + "' height='" + diameter + "'></canvas>")
  explosionCanvasCtx = explosionCanvas[0].getContext("2d")

  explosionCanvasCtx.fillStyle = "red"
  drawCircle(explosionCanvasCtx, projectile.radius, projectile.radius, projectile.radius)
  explosionCanvasCtx.fill()

  explosionCanvasCtx.globalCompositeOperation = 'destination-out' #see https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
  durationPercent = (projectile.startDuration - projectile.duration) / projectile.startDuration
  drawCircle(explosionCanvasCtx, projectile.radius + 20, projectile.radius, projectile.radius)
  explosionCanvasCtx.fill()
  explosionCanvasCtx.globalCompositeOperation = 'source-over' #see https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html

  ctx.drawImage(explosionCanvas[0], projectile.pos.x - projectile.radius, projectile.pos.y - projectile.radius) #center

你有几个选择。

首先,这是一个我们将用来填充圆的函数。

var fillCircle = function(x, y, radius)
{
    context.beginPath();
    context.arc(x, y, radius, 0, 2 * Math.PI, false);
    context.fill();
};

clip()

var clearCircle = function(x, y, radius)
{
    context.beginPath();
    context.arc(x, y, radius, 0, 2 * Math.PI, false);
    context.clip();
    context.clearRect(x - radius - 1, y - radius - 1,
                      radius * 2 + 2, radius * 2 + 2);
};

jsFiddle上看到这个


globalCompositeOperation

var clearCircle = function(x, y, radius)
{
    context.save();
    context.globalCompositeOperation = 'destination-out';
    context.beginPath();
    context.arc(x, y, radius, 0, 2 * Math.PI, false);
    context.fill();
    context.restore();
};

jsFiddle上看到这个


两者都在屏幕上给出了预期的结果,但是在我的情况下性能不够,因为我在每帧绘制和清除很多圆圈以获得效果。最后,我找到了一种不同的方法来获得与我想要的类似效果的方法,只需在圆弧上绘制更粗的线条,但上述方法对于具有不同性能要求的人可能仍然有用。

这样做的问题是它还清除了背景。请参阅我对您的示例的更新:jsfiddle.net/WWH8J/22我将发布我对此问题的解决方案
2021-04-24 20:09:14

用于canvas.getContext("2d").arc(...)在带有背景颜色的区域上画一个圆圈?

var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
context.arc(x, y, r, 0, 2*Math.PI, false);
context.fillStyle = "#FFFFFF";
context.fill();
感谢您花时间回答:) 遗憾的是,在这种情况下,背景不是不透明的,因此不会产生我想要的结果。
2021-04-25 20:09:14
出于性能原因,如果背景不透明,应该这样做而不是剪辑,但不幸的是,这有点不太可能。如果背景有任何透明度,这将不起作用。
2021-05-18 20:09:14