HTML Canvas и использование памяти
Я делаю очень простой графический редактор для мобильных устройств, используя canvas и cordova. К сожалению, javascript - это свинья для памяти, когда задействован холст, поэтому на мобильном все сразу падает.
Я использую cropperjs для обработки обрезки. (Если у вас есть лучший, пожалуйста, дайте мне знать). Кроппер позволяет получать изображения только с помощью базовых URL-адресов данных 64, которые выглядят как огромные потери памяти. Как только изображение обрезается, мне нужно снова отобразить его для обрезки. Бонус - изображение превращается в черно-белое в зависимости от того, как оно было обрезано. Эта часть работает очень хорошо, но опять-таки удваивает объем памяти, вероятно, поскольку в конечном итоге она использует data URL, извлеченные из canvas.
Кнопка на странице вызывает эту функцию. Вот тут-то и начинаются проблемы.
var originalImage = document.createElement('img');
var cropper;
function finish() {
var data=cropper.getCroppedCanvas().toDataURL();
originalImage.onload = function () {
cropper.replace(originalImage, false);
cropper.clear();
originalImage.onload=undefined;
};
originalImage.src=data;
}
Я предполагаю, что главная проблема заключается в том, что data URL настолько массивен, что даже когда его нет в DOM, он сжигает память. Этот небольшой кусочек кода заставляет Chrome и Firefox добавить около 700 МБ оперативной памяти для фотографии 600 КБ. Есть ли лучший способ хранить измененные изображения в памяти? Что-то поменьше? Или, альтернативно, есть ли способ создать новый временный файл и загрузить его? Или я совсем не на том пути?
-Редакты для ответа Blindman67
const originalImage = document.createElement('canvas');
originalImage.ctx = originalImage.getContext("2d");
var cropper; //cropper is created and destroyed on image load, so I can't use const?
function finish() {
const cropped = cropper.getCroppedCanvas();
originalImage.width = cropped.width;
originalImage.height = cropped.height;
originalImage.ctx.drawImage(cropped,0,0);
cropper.clear();
cropper.replace(originalImage , false); //errors, TypeError: t.match is not a function, cropper.min.js (line 11, col 4244)
}
Код, который загружает изображение из файла загрузки
$('#file').on('change', function (ev) {
var f = ev.target.files[0];
var fr = new FileReader();
fr.onload = function (ev2) {
console.dir(ev2);
if (cropper !== undefined) {
cropper.destroy();
}
//Probably something wrong with this part
$('#img').on("load",function(){
originalImage.width = this.width;
originalImage.height = this.height;
originalImage.ctx.drawImage(document.getElementById("img"),0,0);
}).attr('src', ev2.target.result);
//^^^^^
//obvious I've been at this awhile, efficiency went down the tubes \/
var image = document.getElementById("img");
var options = {
viewMode: 0,
dragMode: 'crop',
responsive: true,
autoCrop: false,
movable: false,
scalable: false,
zoomable: false,
zoomOnTouch: false,
zoomOnWheel: false,
ready: cropReady
};
cropper = new Cropper(image, options);
};
fr.readAsDataURL(f);
});
Сама страница в основном
<div >
<img id="img" style="max-width: 100%; max-height: 100%"/>
</div>
Похоже, что использование холстов вместо imgs работает достаточно хорошо.
HTML:
<div class="span-filler">
<img id="img" style="max-width: 100%; max-height: 650px"/>
<canvas id="originalImg" style="display:none;max-width: 100%;max-height: 100%;">Please use Chrome or Firefox
</canvas>
</div>
Сценарии
var cropper;
var gBrightness = 0;
var orgImg = document.getElementById("originalImg");
function finish() {
cropper.replace(orgImg, true); //doesn't need to data URL oddly
var data = cropper.getCroppedCanvas();
orgImg.width = data.width;
orgImg.height = data.height;
orgImg.getContext("2d").drawImage(data, 0, 0);
cropper.replace(orgImg.toDataURL("Image/jpeg"), false); //does need it
cropper.clear();
}
$('#file').on('change', function (ev) {
var f = ev.target.files[0];
var fr = new FileReader();
fr.onload = function (ev2) {
console.dir(ev2);
if (cropper !== undefined) {
cropper.destroy();
}
$('#img').attr('src', ev2.target.result);
var image = document.getElementById("img");
var options = {
viewMode: 0,
dragMode: 'crop',
responsive: true,
autoCrop: false,
movable: false,
scalable: false,
zoomable: false,
zoomOnTouch: false,
zoomOnWheel: false,
ready: cropReady
};
cropper = new Cropper(image, options);
};
fr.readAsDataURL(f);
});
function cropReady() {
var data = cropper.getCroppedCanvas();
orgImg.width = data.width;
orgImg.height = data.height;
orgImg.getContext("2d").drawImage(data, 0, 0);
processImage(); //converts to black and white using web workers
}
//still looking for efficiencies here
function processImage(brightness) {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
canvas.width = orgImg.width;
canvas.height = orgImg.height;
var imgPixels;
var imgPixelsSrc = orgImg.getContext("2d").getImageData(0, 0, orgImg.width, orgImg.height);
var myWorker = new Worker('js/image_editor/imageWorker.js');
myWorker.onmessage = function (e) {
imgPixels = e.data[0];
gBrightness = e.data[2];
ctx.putImageData(imgPixels, 0, 0);
cropper.replace(canvas.toDataURL("image/jpeg"), true);
};
if (brightness === undefined) {
myWorker.postMessage([imgPixelsSrc, true]);
} else {
myWorker.postMessage([imgPixelsSrc, false, brightness]);
}
}
2 ответа
Холст может использоваться как изображение и является HTML-элементом изображения. Нет необходимости конвертировать из холста в изображение только для отображения результатов.
const originalImage = document.createElement('canvas');
originalImage.ctx = originalImage.getContext("2d");
const cropper;
function finish() {
// check documentation to ensure cropper
// is not creating a copy but rather
// is returning just a reference
const cropped = cropper.getCroppedCanvas();
originalImage.width = cropped.width;
originalImage.height = cropped.height;
originalImage.ctx.drawImage(cropped,0,0);
cropper.clear();
}
var img = new Image();
//img.crossOrigin = "Anonymous";
img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
img.onload = function() {
draw(this);
};
function draw(img) {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
img.style.display = 'none';
window['zoomCanvas'] = document.getElementById('zoom');
window['zoomctx'] = document.getElementById('zoom').getContext('2d');
var smoothbtn = document.getElementById('smoothbtn');
var toggleSmoothing = function(event) {
zoomctx.imageSmoothingEnabled = this.checked;
zoomctx.mozImageSmoothingEnabled = this.checked;
zoomctx.webkitImageSmoothingEnabled = this.checked;
zoomctx.msImageSmoothingEnabled = this.checked;
};
smoothbtn.addEventListener('change', toggleSmoothing);
var zoom = function(event) {
var x = event.layerX;
var y = event.layerY;
zoomctx.drawImage(canvas,
Math.abs(x - 100),
Math.abs(y - 100),
200, 200,
0, 0,
200, 200);
};
canvas.addEventListener('mousemove', zoom);
function CROP_IT (e){
//this line will collect all data from canvas
window["IMAGE_CROPED"] = zoomCanvas.toDataURL();
localStorage.setItem( "savedImageData", zoomCanvas.toDataURL("image/png") );
alert(IMAGE_CROPED)
}
canvas.addEventListener('click', CROP_IT , false);
}
<!-- Learn about this code on MDN: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas -->
<canvas id="canvas" width="300" height="227"></canvas>
<canvas id="zoom" width="300" height="227"></canvas>
<div>
<label for="smoothbtn">
<input type="checkbox" name="smoothbtn" checked="false" id="smoothbtn">
Enable image smoothing
</label>