Automatically Crop HTML5 canvas to contents

2019-02-02 05:03发布

问题:

Let's say this is my canvas, with an evil-looking face drawn on it. I want to use toDataURL() to export my evil face as a PNG; however, the whole canvas is rasterised, including the 'whitespace' between the evil face and canvas edges.

+---------------+
|               |
|               |
|     (.Y. )    |
|      /_       |
|     \____/    |
|               |
|               |
+---------------+

What is the best way to crop/trim/shrinkwrap my canvas to its contents, so my PNG is no larger than the face's 'bounding-box', like below? The best way seems to be scaling the canvas, but supposing the contents are dynamic...? I'm sure there should be a simple solution to this, but it's escaping me, with much Googling.

+------+
|(.Y. )|
| /_   |
|\____/|
+------+

Thanks!

回答1:

function cropImageFromCanvas(ctx, canvas) {

var w = canvas.width,
h = canvas.height,
pix = {x:[], y:[]},
imageData = ctx.getImageData(0,0,canvas.width,canvas.height),
x, y, index;

for (y = 0; y < h; y++) {
    for (x = 0; x < w; x++) {
        index = (y * w + x) * 4;
        if (imageData.data[index+3] > 0) {

            pix.x.push(x);
            pix.y.push(y);

        }   
    }
}
pix.x.sort(function(a,b){return a-b});
pix.y.sort(function(a,b){return a-b});
var n = pix.x.length-1;

w = pix.x[n] - pix.x[0];
h = pix.y[n] - pix.y[0];
var cut = ctx.getImageData(pix.x[0], pix.y[0], w, h);

canvas.width = w;
canvas.height = h;
ctx.putImageData(cut, 0, 0);

var image = canvas.toDataURL();
var win=window.open(image, '_blank');
win.focus();

}


回答2:

If I understood well you want to "trim" away all the surronding your image / drawing, and adjust the canvas to that size (like if you do a "trim" command in Photoshop).

Here is how I'll do it.

  1. Run thru all the canvas pixels checking if their alpha component is > 0 (that means that something is drawn in that pixel). Alternativelly you could check for the r,g,b values, if your canvas background is fullfilled with a solid color, for instance.

  2. Get te coordinates of the top most left pixel non-empty, and same for the bottom most right one. So you'll get the coordinates of an imaginay "rectangle" containing the canvas area that is not empty.

  3. Store that region of pixeldata.

  4. Resize your canvas to its new dimensions (the ones of the region we got at step 2.)

  5. Paste the saved region back to the canvas.

Et, voilá :)

Accesing pixeldata is quite slow depending on the size of your canvas (if its huge it can take a while). There are some optimizations around to work with raw canvas pixeldata (I think there is an article about this topic at MDN), I suggest you to google about it.

I prepared a small sketch in jsFiddle that you can use as starting point for your code.

Working sample at jsFiddle

Hope I've helped you.
c:.