Javascript canvas-在矩形中相交的圆孔或如何合并多个弧形路径 [英] Javascript canvas - intersecting circle holes in rectangle or how to merge multiple arc paths
问题描述
我遇到的问题非常简单.这是如何在形状中绘制孔?"的一种变体.这个问题的经典答案是在同一路径上简单绘制两个形状,但顺时针绘制实心而逆时针绘制孔".很好,但是我需要的孔"通常是一个复合形状,由多个圆圈组成.
The issue I have is very straightforward. This is a variation of the "How can I draw a hole in a shape?" question, to which the classic answer is "Simply draw both shapes in the same path, but draw the solid clockwise and the "hole" counterclockwise." That's great but the "hole" I need is often a compound shape, consisting of multiple circles.
视觉描述: http://i.imgur.com/9SuMSWT.png .
jsfiddle: http://jsfiddle.net/d_panayotov/44d7qekw/1/
jsfiddle: http://jsfiddle.net/d_panayotov/44d7qekw/1/
context = document.getElementsByTagName('canvas')[0].getContext('2d');
// green background
context.fillStyle = "#00FF00";
context.fillRect(0,0,context.canvas.width, context.canvas.height);
context.fillStyle = "#000000";
context.globalAlpha = 0.5;
//rectangle
context.beginPath();
context.moveTo(0, 0);
context.lineTo(context.canvas.width, 0);
context.lineTo(context.canvas.width, context.canvas.height);
context.lineTo(0, context.canvas.height);
//first circle
context.moveTo(context.canvas.width / 2 + 20, context.canvas.height / 2);
context.arc(context.canvas.width / 2 + 20, context.canvas.height / 2, 50, 0, Math.PI*2, true);
//second circle
context.moveTo(context.canvas.width / 2 - 20, context.canvas.height / 2);
context.arc(context.canvas.width / 2 - 20, context.canvas.height / 2, 50, 0, Math.PI*2, true);
context.closePath();
context.fill();
已经提出了多种解决方案,我感到我的问题一直是误导性的.因此,这里有更多信息: 我需要矩形区域充当阴影.这是我正在制作的游戏的屏幕截图(希望这不违反规则): http://i .imgur.com/tJRjMXC.png .
Multiple solutions have been proposed and I feel that my question has been misleading. So here's more info: I need the rectangle area to act as a shade. Here's a screenshot from the game I'm making (hope this is not against the rules): http://i.imgur.com/tJRjMXC.png.
- 该矩形的alpha值应小于1.0.
- 孔"中显示的内容是应用阴影之前在画布上绘制的内容.
@markE:
- 或者...淘汰"(擦除)双圆圈...- "destination-out"将画布内容替换为设置的背景. http://jsfiddle.net/d_panayotov/ab21yfgd/-孔是蓝色而不是绿色.
- 另一方面...- "source-atop"要求在定义剪贴蒙版之后绘制内容.在我的情况下,这是低效的(将光绘制为同心圆,阴影区域仍然可见).
- Alternatively...to "knockout" (erase) the double-circles... - "destination-out" replaces the canvas content with the set background. http://jsfiddle.net/d_panayotov/ab21yfgd/ - The holes are blue instead of green.
- On the other hand... - "source-atop" requires content to be drawn after defining the clipping mask. This in my case would be inefficient (Light is drawn as concentric circles, shaded area still visible).
@hobberwickey: 这是静态背景,而不是实际的画布内容.但是,我可以像使用"source-atop"一样使用clip(),但是效率低下.
@hobberwickey: That's a static background, not actual canvas content. I can however use clip() the same way I would use "source-atop" but that would be inefficient.
我目前实施的解决方案: http://jsfiddle.net/d_panayotov/ewdyfnj5/一个>.我只是在主画布内容上绘制了裁剪后的矩形(在内存中的画布中).有更快/更好的解决方案吗?
The solution that I have implemented right now: http://jsfiddle.net/d_panayotov/ewdyfnj5/. I'm simply drawing the clipped rectangle (in an in-memory canvas) over the main canvas content. Is there a faster/better solution?
推荐答案
由于它的简单性,我几乎不敢发布此答案的第一部分,但为什么不只在坚实的背景上填充两个圆圈呢?
I almost dread posting the first part of this answer because of its simplicity, but why not just fill 2 circles on a solid background?
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var r=50;
ctx.fillStyle='rgb(0,174,239)';
ctx.fillRect(0,0,cw,ch);
ctx.fillStyle='white'
ctx.beginPath();
ctx.arc(cw/2-r/2,ch/2,r,0,Math.PI*2);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.arc(cw/2+r/2,ch/2,r,0,Math.PI*2);
ctx.closePath();
ctx.fill();
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=400 height=168></canvas>
或者...淘汰"(删除)双圆圈...
如果您想让2个圆圈向下淘汰"蓝色像素,以使双圆圈透明和&揭示下面的网页背景,然后您可以使用合成敲除"圈子:context.globalCompositeOperation='destination-out
If you want the 2 circles to "knockout" the blue pixels down so the double-circles are transparent & reveal the webpage background underneath, then you can use compositing to "knockout" the circles: context.globalCompositeOperation='destination-out
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var r=50;
// draw the blue background
// The background will be visible only outside the double-circles
ctx.fillStyle='rgb(0,174,239)';
ctx.fillRect(0,0,cw,ch);
// use destination-out compositing to "knockout"
// the double-circles and thereby revealing the
// ivory webpage background below
ctx.globalCompositeOperation='destination-out';
// draw the double-circles
// and effectively "erase" the blue background
ctx.fillStyle='white'
ctx.beginPath();
ctx.arc(cw/2-r/2,ch/2,r,0,Math.PI*2);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.arc(cw/2+r/2,ch/2,r,0,Math.PI*2);
ctx.closePath();
ctx.fill();
// always clean up! Set compositing back to its default
ctx.globalCompositeOperation='source-over';
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=400 height=168></canvas>
另一方面...
如果需要将这些双圆形像素隔离为包含路径,则可以使用合成绘制双圆形而不绘制蓝色背景.
If you need to isolate those double-circle pixels as a containing path, then you can use compositing to draw into the double-circles without drawing into the blue background.
这是另一个例子:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var r=50;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/mm.jpg";
function start(){
// fill the double-circles with any color
ctx.fillStyle='white'
ctx.beginPath();
ctx.arc(cw/2-r/2,ch/2,r,0,Math.PI*2);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.arc(cw/2+r/2,ch/2,r,0,Math.PI*2);
ctx.closePath();
ctx.fill();
// set compositing to source-atop
// New drawings are only drawn where they
// overlap existing (non-transparent) pixels
ctx.globalCompositeOperation='source-atop';
// draw your new content
// The new content will be visible only inside the double-circles
ctx.drawImage(img,0,0);
// set compositing to destination-over
// New drawings will be drawn "behind"
// existing (non-transparent) pixels
ctx.globalCompositeOperation='destination-over';
// draw the blue background
// The background will be visible only outside the double-circles
ctx.fillStyle='rgb(0,174,239)';
ctx.fillRect(0,0,cw,ch);
// always clean up! Set compositing back to its default
ctx.globalCompositeOperation='source-over';
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=400 height=168></canvas>
{回答中还添加了其他想法}
技术要点: xor
合成仅通过翻转像素上的alpha值而不会将像素的r,g,b部分归零.在某些情况下,异或像素的Alpha值将不为零,并且rgb将再次显示.最好使用终点输出"合成,将像素值(r,g,b,a)的所有部分都清零,以免它们意外返回困扰您.
A technical point: xor
compositing works by flipping just the alpha values on pixels but does not also zero-out the r,g,b portion of the pixel. In some cases, the alphas of the xored pixels will be un-zeroed and the rgb will again display. It's better to use 'destination-out' compositing where all parts of the pixel value (r,g,b,a) are zeroed out so they don't accidentally return to haunt you.
请确保... 即使在您的示例中并不重要,您也应始终从maskCtx.beginPath()
开始路径绘制命令.这表示任何先前图形的结束和新路径的开始.
Be sure... Even though it's not critical in your example, you should always begin your path drawing commands with maskCtx.beginPath()
. This signals the end of any previous drawing and the beginning of a new path.
一个选项:我看到您使用同心圆在圆心处引起更大的露底".如果您想要更渐进的显示,则可以使用修剪的阴影(或径向渐变)而不是同心圆来剔除内存中的圆.
One option: I see you're using concentric circles to cause greater "reveal" at the center of your circles. If you want a more gradual reveal, then you could knockout your in-memory circles with a clipped-shadow (or radial gradient) instead of concentric circles.
除此之外,您覆盖内存画布的解决方案应该可以很好地工作(以内存画布使用的内存为代价).
Other than that, you solution of overlaying an in-memory canvas should work well (at the cost of the memory used for the in-memory canvas).
祝你游戏好运!
这篇关于Javascript canvas-在矩形中相交的圆孔或如何合并多个弧形路径的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!