Search code examples
javascripthtml5-canvasgeometryclipping

html5 canvas - merge two clipping regions - James Bond Gunbarrel


I have just started using canvas to get this kind of effect:

"James Bond Gunbarrel View Start" http://www.youtube.com/watch?v=sfXazFp68cE

I managed to get almost there:

http://jsbin.com/uyaker/8/edit

Now as you can see I clip my canvas with two circles (At least I try to) ... but the problem is the region that overlaps is not clipped anymore ...

ctx.clearRect(0, 0, canvas.width, canvas.height);    
ctx.save();
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(canvas.width, 0);
ctx.lineTo(canvas.width, canvas.height);
ctx.lineTo(0, canvas.height);
ctx.closePath();
ctx.moveTo(cx + r, cy);
ctx.arc(cx, cy, r, 0, Math.PI*2, true);
// check if to draw a fixed circle, every 200 pixels for 100 pixels
if(goingright && cx % 200 < 100) {
    ctx.moveTo(cx - cx % 200 + r, cy);
    ctx.arc(cx - cx % 200, cy, r, 0, Math.PI*2, true);
}
ctx.closePath();
ctx.clip();

ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();

Maybe this effect is possible without using clipping but give the canvas a css background-image and draw everything but the circles ... but I don't really know how to do that :/.


Solution

  • Maybe this effect is possible without using clipping ... but I don't really know how to do that

    Yes it is possible, you need to create a path that will do all the work:

    context.beginPath();
    context.moveTo(x, y);
    // Draw your big shape, in your case is a rectangle (4 point)
    context.lineTo(xn, yn);
    context.closePath();
    // Now the context knows that every path that will be added without .beginPath(), will clip the current path
    context.arc(cx, cy, r, 0, Math.PI * 2, true);
    context.closePath();
    context.fill(); // Fill with color all the area except the arc
    

    Example: http://jsfiddle.net/drhWb/

    Saving, restoring and clipping the context are very expensive operations so you should use this approach is the right way you need to go.