您现在的位置是:首页 > 正文

html5 crop,Automatically Crop HTML5 canvas to contents

2024-04-01 00:02:03阅读 0

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!

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

}

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.

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.

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.

Store that region of pixeldata.

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

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.

Hope I've helped you.

c:.

The top voted answer here, as well as the implementations i found online trim one extra pixel which was very apparent when trying to trim text out of canvas. I wrote my own that worked better for me:

var img = new Image;

img.onload = () => {

var canvas = document.getElementById('canvas');

canvas.width = img.width;

canvas.height = img.height;

var ctx = canvas.getContext('2d');

ctx.drawImage(img, 0, 0);

document.getElementById('button').addEventListener('click', ()=>{

autoCropCanvas(canvas, ctx);

document.getElementById('button').remove();

});

};

img.src = 'data:image/png;base64,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';

function autoCropCanvas(canvas, ctx) {

var bounds = {

left: 0,

right: canvas.width,

top: 0,

bottom: canvas.height

};

var rows = [];

var cols = [];

var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

for (var x = 0; x < canvas.width; x++) {

cols[x] = cols[x] || false;

for (var y = 0; y < canvas.height; y++) {

rows[y] = rows[y] || false;

const p = y * (canvas.width * 4) + x * 4;

const [r, g, b, a] = [imageData.data[p], imageData.data[p + 1], imageData.data[p + 2], imageData.data[p + 3]];

var isEmptyPixel = Math.max(r, g, b, a) === 0;

if (!isEmptyPixel) {

cols[x] = true;

rows[y] = true;

}

}

}

for (var i = 0; i < rows.length; i++) {

if (rows[i]) {

bounds.top = i ? i - 1 : i;

break;

}

}

for (var i = rows.length; i--; ) {

if (rows[i]) {

bounds.bottom = i < canvas.height ? i + 1 : i;

break;

}

}

for (var i = 0; i < cols.length; i++) {

if (cols[i]) {

bounds.left = i ? i - 1 : i;

break;

}

}

for (var i = cols.length; i--; ) {

if (cols[i]) {

bounds.right = i < canvas.width ? i + 1 : i;

break;

}

}

var newWidth = bounds.right - bounds.left;

var newHeight = bounds.bottom - bounds.top;

var cut = ctx.getImageData(bounds.left, bounds.top, newWidth, newHeight);

canvas.width = newWidth;

canvas.height = newHeight;

ctx.putImageData(cut, 0, 0);

}

crop canvas

来源:https://stackoverflow.com/questions/11796554/automatically-crop-html5-canvas-to-contents

网站文章