Javascript Canvas - Intersecting Circle Holes In Rectangle Or How To Merge Multiple Arc Paths
Solution 1:
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;}
<canvasid="canvas"width=400height=168></canvas>
Alternatively...to "knockout" (erase) the double-circles...
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;}
<canvasid="canvas"width=400height=168></canvas>
On the other hand...
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.
Here's another example:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var r=50;
var img=newImage();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/mm.jpg";
functionstart(){
// 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;}
<canvasid="canvas"width=400height=168></canvas>
{ Additional thoughts given addition to answer }
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.
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).
Good luck with your game!
Solution 2:
Even easier is just to use clipping and complete circles. Unless there's some reason you NEED to do this with a single path.
var cutoutCircle = function(x, y, r, ctx){
ctx.save()
ctx.arc(x, y, r, 0, Math.PI * 2, false)
ctx.clip()
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
ctx.restore();
}
var myCircles = [{x: 75, y: 100, r: 50}, {x: 125, y: 100, r: 50}],
ctx = document.getElementById("canvas").getContext('2d');
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
for (var i=0; i<myCircles.length; i++){
cutoutCircle(myCircles[i].x, myCircles[i].y, myCircles[i].r, ctx)
}
EDIT: added background to example for better a better demonstration
Post a Comment for "Javascript Canvas - Intersecting Circle Holes In Rectangle Or How To Merge Multiple Arc Paths"