Javascript 从HTML5画布清除圆形区域

Javascript 从HTML5画布清除圆形区域,javascript,html,canvas,html5-canvas,Javascript,Html,Canvas,Html5 Canvas,从画布上清除区域的唯一方法似乎是使用clearRect()命令-我需要清除一个圆(我正在从填充的画布上屏蔽区域,在这种特定情况下是点光源),尽管进行了各种尝试,但这似乎是不可能的 我尝试绘制一个alpha值为0的圆,但除非alpha值更高(与点:p相反),否则什么也不会出现——我假设是因为contex.fill()将其绘制为添加而不是替换 关于如何(快速)清除用于遮罩的圆圈,有什么建议吗?使用canvas.getContext(“2d”).arc(…)在带有背景色的区域上画一个圆圈 var ca

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

我尝试绘制一个alpha值为0的圆,但除非alpha值更高(与点:p相反),否则什么也不会出现——我假设是因为contex.fill()将其绘制为添加而不是替换


关于如何(快速)清除用于遮罩的圆圈,有什么建议吗?

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

使用
.arc
创建圆形笔划,然后使用
.clip()
使其成为当前剪裁区域


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

如果您正在制作一个游戏或其他游戏,压缩每一点性能都很重要,请看看我是如何回答的:

具体而言,答案的编辑会导致:

这里的巨大优势是:

  • 不使用路径(慢速)
  • 不使用剪辑(慢速)
  • 无需保存/还原(因为在不清除所有状态(1)的情况下无法重置剪辑区域,这意味着您还必须使用保存/还原)
(1) 事实上,我和resetClip()已经因为它被放在了官方规范中,但是浏览器实现它还需要一段时间

代码
var ctx=document.getElementById('canvas1').getContext('2d'),
环境光=0.1,
强度=1,
半径=100,
amb='rgba(0,0,0,'+(1-环境光)+')';
添加灯光(ctx、强度、amb、200、200、0、200、200、半径);//第一圈
添加灯光(ctx、强度、amb、250、270、0、250、270、半径);//第二圈
附加光(ctx,强度,电磁轴承,50,370,0,50,370,半径,50);//第三
ctx.fillStyle=amb;
ctx.globalCompositeOperation='xor';
ctx.fillRect(0,0500500);
功能添加灯(ctx、intsy、amb、xStart、yStart、rStart、xEnd、yEnd、rEnd、xOff、yOff){
xOff=xOff | | 0;
约夫=约夫| | 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);
}
画布{
边框:1px纯黑;
背景图像:url('http://placekitten.com/500/500');
}

您有几个选择

首先,这是一个用来填充圆的函数

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

clip()
看看这个


globalCompositeOperation
看看这个



两者都在屏幕上给出了期望的结果,但是在我的情况下,性能不够,因为我在每一帧中绘制并清除了许多圆圈以获得效果。最后,我找到了一种不同的方法,通过在圆弧上画较粗的线来获得与我想要的效果相似的效果,但上述方法可能对具有不同性能要求的人仍然有用。

其中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 );
    }
}
函数clearCircle(x,y,r){
for(var i=0;i
根据要求,这些答案很好。但假设你和我一样,你有额外的要求:

  • 您希望“清除”形状的一部分,该部分可能部分超出要清除的形状的边界
  • 您希望看到形状下方的背景,而不是清除背景 对于第一个需求,解决方案是使用
    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
    
    直径=射弹半径*2
    console.log“”
    explosionCanvas=$(“”)
    explosionCanvasCtx=explosionCanvas[0]。getContext(“2d”)
    explosionCanvasCtx.fillStyle=“红色”
    绘图圈(explosionCanvasCtx,射弹半径,射弹半径,射弹半径)
    explosionCanvasCtx.fill()
    explosionCanvasCtx.globalCompositeOperation='destination out'#请参阅https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
    持续时间百分比=(射弹.startDuration-射弹.duration)/射弹.startDuration
    绘图圈(explosionCanvasCtx,射弹半径+20,射弹半径,射弹半径)
    explosionCanvasCtx.fill()
    explosionCanvasCtx.globalCompositeOperation='source over'#请参阅https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
    ctx.drawImage(explosionCanvas[0],射弹位置x-射弹半径,射弹位置y-射弹半径)#中心
    
    出于性能方面的考虑,如果背景不透明,这是应该做的,而不是剪辑,但不幸的是,这不太可能。如果背景有任何透明度,这将不起作用。谢谢你花时间回答:)不幸的是,在这种情况下,背景不是不透明的,因此不会产生我想要的结果。
      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()
    
      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