HTML Canvas 和内存使用
HTML Canvas and memory usage
我正在使用 canvas 和 cordova 制作一个非常基本的移动图像编辑器。
不幸的是,当涉及到 canvas 时,javascript 会占用大量内存,这会导致所有内容在移动设备上彻底崩溃。
我正在使用 cropperjs 来处理裁剪。 (如果你有更好的,请告诉我)。 Cropper 只允许使用 base 64 数据 URL 检索图像,这似乎是巨大的内存浪费。
裁剪图像后,我需要重新显示它以再次裁剪。
一个好处是图像会根据裁剪方式变成黑白。该部分运行良好,但可能再次使内存大小翻倍,因为它最终使用了从 canvas.
中提取的数据 URL。
页面上的一个按钮调用这个函数。这似乎是麻烦开始的地方。
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;
}
我猜最终的问题是 dataURL 太大,即使不在 DOM 中也会消耗内存。这段代码导致 chrome 和 firefox 为 600kb 的照片增加了大约 700mb 的 RAM 使用。
有没有更好的方法将修改后的图像存储在内存中?更小的东西?或者,有没有办法制作一个新的临时文件并加载它?还是我完全走错了路?
-编辑 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>
-编辑
看起来使用 canvases 代替 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]);
}
}
canvas可以作为图片使用,是HTML图片元素。无需为了显示结果而将 canvas 转换为图像。
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>
我正在使用 canvas 和 cordova 制作一个非常基本的移动图像编辑器。 不幸的是,当涉及到 canvas 时,javascript 会占用大量内存,这会导致所有内容在移动设备上彻底崩溃。
我正在使用 cropperjs 来处理裁剪。 (如果你有更好的,请告诉我)。 Cropper 只允许使用 base 64 数据 URL 检索图像,这似乎是巨大的内存浪费。 裁剪图像后,我需要重新显示它以再次裁剪。 一个好处是图像会根据裁剪方式变成黑白。该部分运行良好,但可能再次使内存大小翻倍,因为它最终使用了从 canvas.
中提取的数据 URL。页面上的一个按钮调用这个函数。这似乎是麻烦开始的地方。
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;
}
我猜最终的问题是 dataURL 太大,即使不在 DOM 中也会消耗内存。这段代码导致 chrome 和 firefox 为 600kb 的照片增加了大约 700mb 的 RAM 使用。 有没有更好的方法将修改后的图像存储在内存中?更小的东西?或者,有没有办法制作一个新的临时文件并加载它?还是我完全走错了路?
-编辑 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>
-编辑 看起来使用 canvases 代替 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]);
}
}
canvas可以作为图片使用,是HTML图片元素。无需为了显示结果而将 canvas 转换为图像。
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>