调整大小&在Web Worker中压缩图像.我可以用一块画布吗

Resize & Compress Image in Web Worker. Can I use a canvas?

本文关键字:我可以 布吗 一块 图像 压缩 amp Worker Web 调整      更新时间:2023-09-26

背景

我现在正在处理客户端选择的图像。

我想对该图像执行两个操作,并输出base64编码的字符串。

  1. 如果图像大小的宽度或高度大于1000,请调整其大小
  2. 使用质量为0.5的jpeg压缩图像

所以现在我将在主脚本中执行以下操作:

$(function() {
  $('#upload').change(function() {
    var URL = window.URL || window.webkitURL;
    var imgURL = URL.createObjectURL(this.files[0]);
    var img = new Image();
    img.onload = function() {
      var canvas = document.createElement('canvas');
      var ctx = canvas.getContext('2d');
      var w0 = img.width;
      var h0 = img.height;
      var w1 = Math.min(w0, 1000);
      var h1 = h0 / w0 * w1;
      canvas.width = w1;
      canvas.height = h1;
      ctx.drawImage(img, 0, 0, w0, h0,
        0, 0, canvas.width, canvas.height);
      // Here, the result is ready, 
      var data_src = canvas.toDataURL('image/jpeg', 0.5);
      $('#img').attr('src', data_src);
      URL.revokeObjectURL(imgURL);
    };
    img.src = imgURL;
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="upload" type="file" accept="image/*" />
<img id="img" />

问题

但是,我的代码仍然可以在手机上工作,上面的过程(调整大小和压缩)不能很快完成。它会导致GUI暂时停止响应。

我希望该过程在另一个线程中工作,使用web worker。所以它不会阻塞用户界面,所以用户体验会更好。

现在问题来了,网络工作者似乎无法处理画布,我该如何解决这个问题?

一些事件驱动的编码

可悲的是,网络工作者还没有准备好浏览器支持。

网络工作者对toDataURL的有限支持意味着需要另一种解决方案。请参阅页面中间的MDN web worker API(ImageData),目前仅适用于Firefox。

看看你的onload,你在一个对onload的阻塞呼叫中完成了所有繁重的工作。在创建新画布、获取其上下文、缩放和toDataURL(不知道revokeObjectURL做什么)的过程中,您正在阻塞UI。在发生这种情况时,您需要让UI获得一些调用。因此,一点事件驱动的处理将有助于减少故障,如果不是让它变得不明显的话。

尝试按如下方式重写onload函数。

// have added some debugging code that would be useful to know if
// this does not solve the problem. Uncomment it and use it to see where
// the big delay is.
img.onload = function () {
    var canvas, ctx, w, h, dataSrc, delay; // hosit vars just for readability as the following functions will close over them
                                    // Just for the uninitiated in closure.
    // var now, CPUProfile = [];  // debug code 
    delay = 10; // 0 could work just as well and save you 20-30ms
    function revokeObject() { // not sure what this does but playing it safe
        // as an event.
        // now = performance.now(); // debug code
        URL.revokeObjectURL(imgURL);
        //CPUProfile.push(performance.now()-now); // debug code
        // setTimeout( function () { CPUProfile.forEach ( time => console.log(time)), 0);
    }
    function decodeImage() {
        // now = performance.now(); // debug code
        $('#img').attr('src', dataSrc);
        setTimeout(revokeObject, delay); // gives the UI a second chance to get something done.
        //CPUProfile.push(performance.now()-now); // debug code
    }
    function encodeImage() {
        // now = performance.now(); // debug code
        dataSrc = canvas.toDataURL('image/jpeg', 0.5);
        setTimeout(decodeImage, delay); // gives the UI a second chance to get something done.
        //CPUProfile.push(performance.now()-now); // debug code
    }
    function scaleImage() {
        // now = performance.now(); // debug code
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
        setTimeout(encodeImage, delay); // gives the UI a second chance to get something done.
        //CPUProfile.push(performance.now()-now); // debug code
    }
    // now = performance.now(); // debug code
    canvas = document.createElement('canvas');
    ctx = canvas.getContext('2d');
    w = Math.min(img.width, 1000);
    h = img.height / img.width * w;
    canvas.width = w;
    canvas.height = h;
    setTimeout(scaleImage, delay); // gives the UI a chance to get something done.
    //CPUProfile.push(performance.now()-now); // debug code
};

setTimeout允许当前调用退出,从而释放调用堆栈,并允许UI在DOM上获取其mitts。我已经给出了10ms,就我个人而言,我会从0ms开始,因为调用堆栈访问没有被阻止,但我正在安全地进行

如果运气好的话,这个问题会大大减少。如果它仍然是一个不可接受的延迟,那么我可以查看CPU配置文件,看看是否无法通过瞄准瓶颈找到解决方案。我的猜测是toDataURL是负载所在的地方。如果是,一个可能的解决方案是找到一个用javascript编写的JPG编码器,它可以转换为事件驱动的编码器。

问题不在于处理数据需要多长时间,而在于阻塞UI需要多长时间。