Подтвердить что ты не робот

Автоматически обрезать холст HTML5 до содержимого

Скажем, это мой холст, со злобным лицом, нарисованным на нем. Я хочу использовать toDataURL() для экспорта моего злого лица в виде PNG; однако весь холст растеризуется, включая "пробел" между злым лицом и краями холста.

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

Каков наилучший способ обрезать/обрезать/обрезать мой холст до его содержимого, поэтому мой PNG не больше, чем граничная рамка лица, как показано ниже? Лучший способ - это масштабирование холста, но предположим, что содержимое динамическое...? Я уверен, что для этого должно быть простое решение, но оно ускользает от меня, с большим количеством Googling.

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

Спасибо!

4b9b3361

Ответ 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

Если я правильно понял, вы хотите "обрезать" все ваши изображения/чертежи и настроить холст на этот размер (например, если вы делаете команду "trim" в Photoshop).

Вот как я это сделаю.

  • Запустите все пиксели холста, проверяя, есть ли их альфа-компонент > 0 (это означает, что в этом пикселе что-то нарисовано). Альтернативно вы можете проверить значения r, g, b, если ваш фон холста заполнен сплошным цветом, например.

  • Получить те координаты верхнего левого пикселя непустые, а также для самого нижнего правого. Таким образом, вы получите координаты воображаемого "прямоугольника", содержащего область холста, которая не является пустой.

  • Сохраните эту область пикселей.

  • Измените размер вашего холста на его новые размеры (те области, которые мы получили на шаге 2.)

  • Вставьте сохраненную область обратно в холст.

Et, voilá:)

Приблизительная пиксельная точка довольно медленная в зависимости от размера вашего холста (если ее огромная она может занять некоторое время). Есть несколько оптимизаций для работы с необработанной пиксельной таблицей (я думаю, что есть статья об этой теме в MDN), я предлагаю вам об этом Google.

Я подготовил небольшой эскиз в jsFiddle, который вы можете использовать в качестве отправной точки для своего кода.

Рабочий пример в jsFiddle

Надеюсь, я помог тебе.
C:.

Ответ 3

Здесь проголосовали лучшие, а также реализации, которые я нашел в Интернете, обрезать один дополнительный пиксель, что было очень заметно при попытке обрезать текст из канвы. Я написал свой, который работал лучше для меня:

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);
	}
<canvas id=canvas style='border: 1px solid pink'></canvas>
<button id=button>crop canvas</button>