Javascript canvas - intersecting circle holes in rectangle or how to merge multiple arc paths -


the issue have straightforward. variation of "how can draw hole in shape?" question, classic answer "simply draw both shapes in same path, draw solid clockwise , "hole" counterclockwise." that's great "hole" need compound shape, consisting of multiple circles.

visual description: http://i.imgur.com/9sumswt.png.

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

edit:

multiple solutions have been proposed , feel question has been misleading. here's more info: need rectangle area act shade. here's screenshot game i'm making (hope not against rules): http://i.imgur.com/tjrjmxc.png.

  • the rectangle should able have alpha less 1.0.
  • the contents, displayed in "holes" whatever drawn on canvas before applying shade.

@marke:

  • alternatively...to "knockout" (erase) double-circles... - "destination-out" replaces canvas content set background. http://jsfiddle.net/d_panayotov/ab21yfgd/ - holes blue instead of green.
  • on other hand... - "source-atop" requires content drawn after defining clipping mask. in case inefficient (light drawn concentric circles, shaded area still visible).

@hobberwickey: that's static background, not actual canvas content. can use clip() same way use "source-atop" inefficient.

the solution have implemented right now: http://jsfiddle.net/d_panayotov/ewdyfnj5/. i'm drawing clipped rectangle (in in-memory canvas) on main canvas content. there faster/better solution?

i dread posting first part of answer because of simplicity, why not fill 2 circles on solid background?

enter image description here

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>

alternatively...to "knockout" (erase) double-circles...

if want 2 circles "knockout" blue pixels down double-circles transparent & reveal webpage background underneath, can use compositing "knockout" circles: context.globalcompositeoperation='destination-out

enter image description here

var canvas=document.getelementbyid("canvas");  var ctx=canvas.getcontext("2d");  var cw=canvas.width;  var ch=canvas.height;    var r=50;      // draw blue background  // background visible outside double-circles  ctx.fillstyle='rgb(0,174,239)';  ctx.fillrect(0,0,cw,ch);      // use destination-out compositing "knockout"   // double-circles , thereby revealing  // ivory webpage background below  ctx.globalcompositeoperation='destination-out';    // draw double-circles  // , "erase" 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();    // clean up! set compositing default  ctx.globalcompositeoperation='source-over';
body{ background-color: ivory; }  #canvas{border:1px solid red;}
<canvas id="canvas" width=400 height=168></canvas>

on other hand...

if need isolate double-circle pixels containing path, can use compositing draw double-circles without drawing blue background.

here's example:

enter image description here

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 double-circles 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 source-atop    // new drawings drawn    //    overlap existing (non-transparent) pixels    ctx.globalcompositeoperation='source-atop';        // draw new content    // new content visible inside double-circles    ctx.drawimage(img,0,0);      // set compositing destination-over    // new drawings drawn "behind"     //    existing (non-transparent) pixels    ctx.globalcompositeoperation='destination-over';      // draw blue background    // background visible outside double-circles    ctx.fillstyle='rgb(0,174,239)';    ctx.fillrect(0,0,cw,ch);      // clean up! set compositing default    ctx.globalcompositeoperation='source-over';    }
body{ background-color: ivory; }  #canvas{border:1px solid red;}
<canvas id="canvas" width=400 height=168></canvas>

{ additional thoughts given addition answer }

a technical point: xor compositing works flipping alpha values on pixels not zero-out r,g,b portion of pixel. in cases, alphas of xored pixels un-zeroed , rgb again display. it's better use 'destination-out' compositing parts of pixel value (r,g,b,a) zeroed out don't accidentally return haunt you.

be sure... though it's not critical in example, should begin path drawing commands maskctx.beginpath(). signals end of previous drawing , beginning of new path.

one option: see you're using concentric circles cause greater "reveal" @ center of circles. if want more gradual reveal, knockout in-memory circles clipped-shadow (or radial gradient) instead of concentric circles.

other that, solution of overlaying in-memory canvas should work (at cost of memory used in-memory canvas).

good luck game!


Comments

Popular posts from this blog

Payment information shows nothing in one page checkout page magento -

tcpdump - How to check if server received packet (acknowledged) -