从 HTML5 Canvas 中清除圆形区域

问题描述 投票:0回答:7

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

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

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

javascript html canvas html5-canvas
7个回答
49
投票

使用

.arc
创建圆形描边,然后使用
.clip()
将其设为当前剪切区域。

然后您可以使用

.clearRect()
擦除整个画布,但只有剪切区域会发生变化。


16
投票

如果您正在制作游戏或其他需要压缩每一点性能的东西,请看看我是如何做出这个答案的:Canvas - 在所有完全透明的区域中填充一个矩形

具体来说,对答案的编辑导致了这一点: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>


15
投票

考虑到要求,这些答案都很好。 但假设您像我一样并且有其他要求:

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

对于第一个要求,解决方案是使用

context.globalCompositeOperation = 'destination-out'
蓝色是第一个形状,红色是第二个形状。 如您所见,
destination-out
从第一个形状中删除了该部分。

enter image description here

这是一些示例代码:

  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
    

13
投票
您有几个选择。

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

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 上查看此内容。


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


0
投票
使用

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

0
投票
其中 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 ); } }
    

0
投票
基于 MDN 裁剪教程中的代码 (

https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Compositing#a_clip_example)

// Create a circular clipping path ctx.beginPath(); ctx.arc(0, 0, 60, 0, Math.PI * 2, true); ctx.clip();
    
© www.soinside.com 2019 - 2024. All rights reserved.