HTML Canvas и использование памяти

1

Я создаю очень простой редактор изображений для мобильных устройств, используя холст и кордову. К сожалению, javascript - это свинья для памяти, когда задействован холст, и это приводит к краху на мобильных устройствах.

Я использую cropperjs для обработки обрезки. (Если у вас есть лучший, пожалуйста, дайте мне знать). Cropper позволяет получать изображения только с использованием базовых 64 URL-адресов данных, которые, как представляется, являются огромными потерями памяти. Как только изображение обрезается, мне нужно снова отобразить его для обрезки. Бонус - это обращение к черно-белому изображению, основанное на том, как оно было обрезано. Эта часть работает очень хорошо, но, возможно, удваивает размер памяти, поскольку, в конечном счете, использует dataURL, извлеченные из холста.

Кнопка на странице вызывает эту функцию. Вот тут и начинается проблема.

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 он не сжигает память. Эта небольшая часть кода заставляет хром и firefox добавлять около 700 мб оперативной памяти для фото 600 КБ. Есть ли лучший способ хранить измененные изображения в памяти? Что-то меньше? Или, альтернативно, есть ли способ создать новый файл temp и загрузить его? Или я ошибаюсь?

-Edits для 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>

-Edit Похоже, что использование холстов вместо 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
cordova
cropperjs

2 ответа

1
Лучший ответ

Холст может использоваться как изображение и является элементом изображения 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();
}
  • 0
    Чтобы внести эти изменения потребовалось немного времени, но, cropper.replace(originalImage , true); TypeError: t.match is not a function из cropper.min.js.
  • 0
    HTMLCanvasElement не является HTMLImageElement. Не потому, что оба объекта могут быть переданы в drawImage что они одинаковы ...
Показать ещё 6 комментариев
1

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>
  • 1
    Хотя это не совсем то, что мне нужно, так как эта страница предназначена для фотографирования документов, поэтому она требует точной обрезки. Это все еще довольно интересно и может помочь мне в других местах. Тогда спасибо :)
  • 0
    Вот пример без сглаживания и изменения размера ....

Ещё вопросы

Сообщество Overcoder
Наверх
Меню