1

I know that I can clip a canvas by creating a path with getContext('2d') and setting globalCompositeOperation.

I've noticed that sometimes I'm able to clip a canvas with -webkit-clip-path, or clip-path on other browsers (I'm on Chrome), and sometimes I'm not able to:

Using this HTML:

<canvas width="300" height="60"></canvas>

and CSS:

canvas { -webkit-clip-path: polygon(50% 33%, 75% 10%, 80% 80%, 60% 75%, 40% 60%, 20% 10%, 40% 20%, 50% 33%);
}

produces this:

enter image description here

Which appears to be correct.

However, I've noticed that if I change the height of the canvas, it fails to clip:

<canvas width="300" height="250"></canvas>

produces:

enter image description here

My assumption was that it has a problem clipping on floating points (where the percentages clip between pixels instead of on pixels), but changing from percents to pixel coordinates doesn't clip.

*Here are links to their jsfiddle pages respectively:

Does anyone know why one works but the other doesn't?

Is there a stable way to clip canvas elements with CSS, or do I need to use the canvas context methods?

The reason I ask is that I'd like to use less js where possible. I have a string of coordinates which I can easily put into css; whereas, to use the ctx.beginPath()...ctx.moveTo()...ctx.lineTo()...ctx.lineTo()... method I'll need to do a for loop for the points.

Also, I'm very curious as to why the first example worked, if anyone can explain that. Thanks! :)

bozdoz
  • 12,550
  • 7
  • 67
  • 96

2 Answers2

2

The clip-path is relative new and could be prone to errors (didn't work for me in Aurora).

For a stable result I would suggest just using canvas' clip() method (you don't need composite for this).

You can provide the points in this way (here percentages):

var path = [50, 33, 75, 10, 80, 80, 60, 75, 40, 60, 20, 10, 40, 20, 50, 33];

Almost as easy as defining in CSS. Then have a function to parse it:

function addClip(context, path) {

    var w = context.canvas.width,
        h = context.canvas.height;

    context.beginPath();
    context.moveTo(path[0] * w / 100, path[1] * h / 100);
    for(var i = 2; i < path.length; i+=2) {
        context.lineTo(path[i] * w / 100, path[i+1] * h / 100);
    }
    context.closePath();
    context.clip();
}

Result:

enter image description here

DEMO HERE

(The clip is set before the drawing operations take place).

Just put your drawing operations in a function which you can call when window is re-sized as well (shown in demo above).

Update

As to anti-alias: there is actually applied anti-alias to the image but because of the red color it can be hard to detect it depending on type of screen and perhaps browser. An enlarged version:

enter image description here

  • Thanks Ken. That's a good working example. I was actually going to avoid the `clip()` method so that I can get the polygon with anti-aliasing (smoother edges, like the CSS would give): http://jsfiddle.net/bozdoz/TB9rX/ Any idea why my first example worked but the others didn't? – bozdoz Jul 09 '13 at 19:36
  • @bozdoz I can only speculate but it appear to be a bug. If element is resized after clip is applied the clip seem to be lost. You could test by re-applying the css-class again with JS: `document.getElementById('canvas').className = 'canvas';` (of course you'd need to change the CSS so that the rule is class). –  Jul 09 '13 at 19:45
  • @bozdoz as to anti-aliasing: try with this line: `context.translate(0.5, 0.5);` Updated fiddle: http://jsfiddle.net/AbdiasSoftware/CmaUy/2/ (the image in the answer do have anti-aliasing, but with red color it can be hard to see it depending on type of screen). –  Jul 09 '13 at 19:51
  • I think it varies for browsers: http://stackoverflow.com/questions/9536257/how-to-anti-alias-clip-edges-in-html5-canvas-under-chrome-windows – bozdoz Jul 09 '13 at 20:04
1

I've never worked with -webkit-clip-path:, but just on general principles I'd try, as a workaround, applying the clip path to an element containing the canvas instead of the canvas itself.

 <div class='canvas-wrapper'><canvas></canvas></div>
 .canvas-wrapper {
    display: table;   /* shrinkwrap around canvas */
    -webkit-clip-path: ...;
 }
Kevin Reid
  • 37,492
  • 13
  • 80
  • 108