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>