Search code examples
javascripthtmlcanvasclip

canvas - Substract shape from a clipped canvas


I want to clip an annulus (i.e. ring) from an image via javascripts canvas. I already have an approach but i think its too inelegant (and I really dont understand why this works, and why it doesnt just result in a smaller circle).

see this jsfiddle

    context.drawImage(imageObj, 0, 0, 500, 500);

    //crop outer circle
    context2.beginPath();
    context2.arc(250, 250, 200, 0, 2 * Math.PI, false);
    context2.closePath();
    context2.clip();

    //draw circle
    context2.drawImage(canvas,0,0);

    //crop inner circle
    context2.beginPath();
    context2.arc(250, 250, 100, 0, 2 * Math.PI, false);
    context2.closePath();
    context2.clip();

    //clear context 2
    context2.clearRect(0,0,500,500)

    // finally draw annulus
    context2.drawImage(canvas2,0,0);

is there a better way to do this?


Solution

  • This does work because clipping areas called by clip method do stack.

    IMO, this is indeed not the best way to do it, as you definitely need to call ctx.save(); before clipping and ctx.restore() afterward, which are really heavy methods.

    My preferred way is to use compositing :

    var ctx = canvas.getContext('2d');
    
    var imageObj = new Image();
    
    imageObj.onload = function() {
    
      ctx.drawImage(imageObj, 0, 0, 500, 500);
      // keep only the outer circle
      ctx.globalCompositeOperation = 'destination-in';
      ctx.beginPath();
      ctx.arc(250, 250, 200, 0, 2 * Math.PI, false);
      ctx.fill();
      // remove the inner one
      ctx.globalCompositeOperation = 'destination-out';
      ctx.beginPath();
      ctx.arc(250, 250, 100, 0, 2 * Math.PI, false);
      ctx.fill();
      // reset gCO
      ctx.globalCompositeOperation = 'source-over';
    
    };
    imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
    <canvas id="canvas" width="500" height="500"></canvas>