Javascript 从 HTML5 Canvas 中清除圆形区域

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10396991/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 01:08:59  来源:igfitidea点击:

clearing circular regions from HTML5 Canvas

javascripthtmlcanvashtml5-canvas

提问by David Burford

It appears the only way to clear a region from a canvas is to use the clearRect() command - I need to clear a circle (I am masking out areas from a filled canvas, point lights in this specific case) and despite all attempts it does not seem possible.

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

I tried drawing a circle with an alpha value of 0 but simply nothing would appear unless the alpha was higher (which is counter to the point :P) - I assume because a contex.fill() draws it as an add rather than a replace.

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

Any suggestions on how I might be able to (quickly) clear circles for mask purposes?

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

回答by Alnitak

Use .arcto create a circular stroke and then use .clip()to make that the current clipping region.

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

Then you can use .clearRect()to erase the whole canvas, but only the clipped area will change.

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

回答by Simon Sarris

If you're making a game or something where squeezing every bit of performance matters, have a look at how I made this answer: Canvas - Fill a rectangle in all areas that are fully transparent

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

Specifically, the edit of the answer that leads to this: http://jsfiddle.net/a2Age/2/

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

The huge plusses here:

这里的巨大优势:

  • No use of paths (slow)
  • No use of clips (slow)
  • No need for save/restore (since there's no way to reset a clipping region without clearing all state(1), it means you must use save/restore also)
  • 不使用路径(慢)
  • 不使用剪辑(慢)
  • 无需保存/恢复(因为在不清除所有状态(1)的情况下无法重置剪辑区域,这意味着您也必须使用保存/恢复)

(1) I actually complained about thisand resetClip() has been put in the offical spec because of it, but it will be a while before browsers implement it.

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

Code

代码

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>

回答by Daniel Kaplan

Given the requirements, these answers are fine. But lets say you're like me and you have additional requirements:

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

  1. You want to "clear" a part of a shape that may be partially outside the bounds of the shape you're clearing.
  2. You want to see the background underneath the shape instead of clearing the background.
  1. 您想要“清除”可能部分超出您正在清除的形状边界的形状的一部分。
  2. 您希望看到形状下方的背景,而不是清除背景。

For the first requirement, the solution is to use context.globalCompositeOperation = 'destination-out'The blue is the first shape and the red is the second shape. As you can see, destination-outremoves the section from the first shape.

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

enter image description here

enter image description here

Here's some example code:

下面是一些示例代码:

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

Here's the potential problem with this: The second fill()will clear everythingunderneath it, including the background. Sometimes you'll want to only clear the first shape but you still want to see the layers that are underneath it.

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

The solution to that is to draw this on a temporary canvas and then drawImageto draw the temporary canvas onto your main canvas. The code will look like this:

解决方案是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

回答by Drew Noakes

You have a few options.

你有几个选择。

Firstly, here's a function we'll use to fill a circle.

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

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


clip()

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

See this on jsFiddle.

jsFiddle上看到这个。



globalCompositeOperation

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

See this on jsFiddle.

jsFiddle上看到这个。



Both gave the desired result on screen, however the performance wasn't sufficient in my case as I was drawing and clearing a lot of circles each frame for an effect. In the end I found a different way to get a similar effect to what I wanted by just drawing thicker lines on an arc, but the above may still be useful to someone having different performance requirements.

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

回答by Alex

Where x = left position, y = right position, r = radius, and ctx = your canvas:

其中 x = 左侧位置,y = 右侧位置,r = 半径,而 ctx = 您的画布:

function clearCircle( x , y , r ){
    for( var i = 0 ; i < Math.round( Math.PI * r ) ; i++ ){
        var angle = ( i / Math.round( Math.PI * r )) * 360;
        ctx.clearRect( x , y , Math.sin( angle * ( Math.PI / 180 )) * r , Math.cos( angle * ( Math.PI / 180 )) * r );
    }
}

回答by Ozzy

Use canvas.getContext("2d").arc(...)to draw a circle over the area with the background colour?

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