简体   繁体   English

清除HTML5 Canvas中的圆形区域

[英]clearing circular regions from HTML5 Canvas

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? 关于我如何能够(快速)清除圆圈以进行掩模的任何建议?

Use .arc to 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()来擦除整个画布,但只有剪切区域会改变。

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 this and 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> 

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. 您希望在形状下方看到背景而不是清除背景。

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. 对于第一个要求,解决方案是使用context.globalCompositeOperation = 'destination-out'蓝色是第一个形状,红色是第二个形状。 As you can see, destination-out removes the section from the first shape. 如您所见, destination-out从第一个形状中删除该部分。

在此输入图像描述

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 everything underneath it, including the background. 这是潜在的问题:第二个fill()将清除它下面的所有内容,包括背景。 Sometimes you'll want to only clear the first shape but you still want to see the layers that are underneath it. 有时您只想清除第一个形状,但仍希望看到它下面的图层。

The solution to that is to draw this on a temporary canvas and then drawImage to draw the temporary canvas onto your main canvas. 解决方法是在临时画布上绘制它,然后使用drawImage将临时画布绘制到主画布上。 The code will look like this: 代码如下所示:

  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

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

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

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. 最后,我发现了一种不同的方法,通过在弧上绘制较粗的线条来获得与我想要的相似的效果,但上述对于具有不同性能要求的人来说仍然有用。

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM