globalCompositeOperation 和同心、空心、移动的形状
Posted
技术标签:
【中文标题】globalCompositeOperation 和同心、空心、移动的形状【英文标题】:globalCompositeOperation and concentric, hollow, moving shapes 【发布时间】:2014-05-23 05:25:40 【问题描述】:我正在努力实现以下目标:
在画布上绘制了许多同心圆(或环)。每个圆圈都有一个“洞”,因此在其后面绘制的较小圆圈是部分可见的。每一帧(我们使用 window.requestAnimationFrame 来渲染)每个圆/形状/环的半径都会略微增加。
图像here 中描绘了一个带有两个环的场景。
代码:
function draw()
drawBgr();
for (var i = 0, len = rings.length; i < len; i++)
rings[i].draw();
function drawBgr()
context.globalCompositeOperation = "source-over";
context.clearRect(0, 0, WIDTH, HEIGHT);
context.rect(0, 0, WIDTH, HEIGHT);
context.fillStyle = '#FFFFFF';
context.fill();
function squareRing(ring) //called by rings[i].draw();
context.globalCompositeOperation = "source-over";
context.fillRect(ring.centerX - ring.radius / 2, ring.centerY - ring.radius / 2, ring.radius, ring.radius);
context.globalCompositeOperation = "source-out";
context.beginPath();
context.arc(CENTER_X, CENTER_Y, ring.radius, 0, 2 * Math.PI, false);
//context.lineWidth = RING_MAX_LINE_WIDTH * (ring.radius / MAX_SIDE);
context.fillStyle = '#000000';
context.fill();
context.globalCompositeOperation = "source-over";
这到底是什么问题?我在绘制圆圈之前调用 clearRect 。请参阅“我实际得到的”图像。这是在多个帧上绘制 SINGLE RING 的结果。我不应该得到任何与中间有一个空心正方形的黑色圆圈不同的东西。 (请注意,半径每帧都在增加。)
我确实意识到切换 globalCompositeOperation 可能不足以达到我想要的效果。如何在画布上绘制的对象中绘制一个“洞”而不擦除我要修改的对象下方“洞”中的所有内容?
This 是我用作 globalCompositeOperation 值参考的教程。
我使用的是 Firefox 28.0。
【问题讨论】:
【参考方案1】:我不会尝试使用 globalCompositeOperation,因为我发现很难弄清楚几次迭代后会发生什么,如果之前没有清除画布,那就更难了。
我更喜欢使用剪辑,这让我明白了:
http://jsbin.com/guzubeze/1/edit?js,output
那么,要在平局中建立一个“洞”,如何使用剪辑? -->> 定义一个正向裁剪子路径,在这个区域内,剪掉一个负向部分,这次使用顺时针的子路径:
剪辑必须用一个路径完成,所以不能使用 rect() :它每次都开始一个路径,并且不允许选择顺时针 (:-)),所以你必须定义这两个函数只会创建所需的子路径:
// clockwise sub-path of a rect
function rectPath(x,y,w,h)
ctx.moveTo(x,y);
ctx.lineTo(x+w,y);
ctx.lineTo(x+w,y+h);
ctx.lineTo(x,y+h);
// counter-clockwise sub-path of a rect
function revRectPath(x,y,w,h)
ctx.moveTo(x,y);
ctx.lineTo(x,y+h);
ctx.lineTo(x+w,y+h);
ctx.lineTo(x+w,y);
然后你就可以写你的绘图代码了:
function drawShape(cx, cy, d, scale, rotation)
ctx.save();
ctx.translate(cx,cy);
scale = scale || 1;
if (scale !=1) ctx.scale(scale, scale);
rotation = rotation || 0;
if (rotation) ctx.rotate(rotation);
// clip with rectangular hole
ctx.beginPath();
var r=d/2;
rectPath(-r,-r, d, d);
revRectPath(-0.25*r,-0.8*r, 0.5*r, 1.6*r);
ctx.closePath();
ctx.clip();
ctx.beginPath();
// we're clipped !
ctx.arc(0,0, r, 0, 2*Math.PI);
ctx.closePath();
ctx.fill();
ctx.restore();
编辑:
为了记录,有一个更简单的方法来绘制所要求的方案:只画一个圆圈,然后在里面逆时针画一个矩形。您填充的将是矩形之外的圆圈内的部分,这就是您想要的:
function drawTheThing(x,y,r)
ctx.beginPath();
ctx.arc(x ,y, r, 0, 2*Math.PI);
revRectPath(x-0.25*r, y-0.8*r, 0.5*r, 1.6*r);
ctx.fill();
ctx.closePath();
(我不发布图片:它是一样的)。
根据您的需要,如果您更改平局或者如果您想引入某种通用性,请使用第一个或第二个。 如果以后不改方案,第二种方案更简单=>更好。
【讨论】:
这太棒了!请注意,您实际上并不需要两条矩形路径,您可以这样做: 1. 绘制圆形(顺时针) 2. 绘制矩形路径(逆时针) 3. 调用 ctx.clip() 4. 调用 ctx.fill() 谢谢。是的,我知道有一种简单的方法,同样我们不需要缩放,只需将半径乘以比例即可。但我想做一些更通用的东西,以便于理解,并使绘图的更改更容易。 我想说的是,我认为没有充分的理由拥有: rectPath(-r,-r, d, d); revRectPath(-0.25*r,-0.8*r, 0.5*r, 1.6*r);.但这可能是因为现在我没有引用掌握 ctx.save/restore() 的目的。无论如何,非常感谢你的真棒和彻底的回答!这真的很有帮助! 第一个矩形说:只在那个周边(剪辑'in')内绘制,第二个矩形说:不要在那个周边绘制,所以它像一个“洞”矩形,里面什么都没有画。保存/恢复在这里是为了避免剪裁保持永久:剪裁,绘制,然后恢复到新的先前状态。祝你好运! 哎呀,我意识到了一些事情,这更简单:查看我的编辑。以上是关于globalCompositeOperation 和同心、空心、移动的形状的主要内容,如果未能解决你的问题,请参考以下文章
Canvas学习:globalCompositeOperation详解
webgl 中的 globalcompositeoperation 等效项
在画布中使用 globalCompositeOperation 屏蔽多个形状
分几个阶段使用 globalCompositeOperation