10

后台设置

我有一个 Web 应用程序,它处理从一组其他图像创建图像。我选择这样做的方法是读取一组图像并将它们放置在 HTML 画布上。然后,我将每个画布作为 jpeg 导出到第三方 API,并使用toDataURL并将其转换为 Blob。我面临的问题是我有许多这样的画布都将数据导出为 jpg 并且它消耗了大量资源。当每个画布尝试调用toDataURL.

问题

我发现调用画布的toDataUrl()ortoBlob()可能会非常慢,尤其是对于大画布尺寸。我想利用网络工作者的多线程特性。

首先,我尝试传入画布对象,但抛出了一个错误。事实证明,对象是一个问题,而且似乎它们要么被转换为字符串,要么在无法克隆时失败。无论哪种方式,我发现传递上下文的图像数据确实有效。数据以原始 RGB 值的形式作为Uint8ClampedArray画布上下文的方法传递getImageData()

主.js

var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var worker = new Worker('myWorker.js');
worker.postMessage({
  image: context.getImageData(0, 0, canvas.width, canvas.height)
});

myWorker.js

this.onmessage = function(e) {
  // GOAL: turn e.data.image into an image blob or dataUrl and return it.
  // e.g. this.postMessage(new Blob([e.data.image.data], {type: 'image/jpg'});
}

我认为归结为知道如何将Uint8ClampedArray保存 RGB 信息的 a 转换为 jpg/png 数据。

我认为这可能有用的原因是我相信getImageData只是从画布上下文中复制现有数据结构,因此不像toDataUrl. 我在调用类似于以下代码块的内容时捕获了 cpu 配置文件:

var image = context.getImageData(0, 0, canvas.width, canvas.height)
var dataUrl = canvas.toDataURL('image/jpeg');

并得到:

getImageData 和 toDataURL 的性能结果

所以,考虑到这一点,我想把这个过程的重担转移到一个网络工作者身上。我什至不介意网络工作者内部是否需要更长的时间,只要它发生在另一个进程中。

关于它的一些额外想法:

  • 添加一个额外的库来进行转换是可以的,但是提供如何将外部库添加为 web worker 文件的依赖项的奖励积分。现在我正在为应用程序使用 browserify。也许为网络工作者创建另一个浏览器化包?
  • 我最终需要一个 jpeg(对于第三方 API),因此将其转换为 png 只是作为转换为 jpeg 的一个步骤。
  • 我尝试降低 中encoderOptions的第二个选项toDataURL,作为加快进程的一种方式,但我没有看到太大的变化
4

1 回答 1

6

- - 更新 - -

我想我会以 npm 库的形式分享我的解决方案: https ://www.npmjs.com/package/jpeg-web-worker 。它解释了如何利用提供的网络工作者为您完成繁重的工作。

---------------------

我得到了一个适合我的解决方案,加快了应用程序和页面的响应速度,同时仍然生成了新图像。

这是应用程序代码:

应用程序

var canvas = $('#myCanvas')[0];
var context = canvas.getContext('2d');
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
var worker = new Worker('myWorker.js');
worker.postMessage({
  image: imageData
});
worker.onmessage = function(e) {
  var blob = new Blob( [e.data.data], {type: 'image/jpeg'} );
  // use blob
}

这是工人代码:

工人

this.onmessage = function(e) {
  var jpgInfo = encode(e.data.image, 50);
  this.postMessage(jpgInfo);
}

function encode() { ... } // ported from jpeg-js

显然,这个答案的大部分来自encode函数。这个函数是从 npm 模块jpeg-js修改的,更具体地说,是文件encoder.js。我通过将整个encoder.js 文件复制到myWorker.js 中来移植encode 函数。它不是很小,但它也非常独立,这使它变得容易。我留下的唯一问题是修改代码,以便它在构建它的 node.js 环境之外工作。

事实证明这相对容易:

  1. 将“const”变量声明转换为“var”
  2. 删除对Buffer. 这是一个两步过程。首先,删除顶部的 atob 定义(因为它不是必需的)。其次,new Unit8Array在 this.encode 函数的末尾返回 a。当前版本实际上在缓冲区引用的正上方对此进行了注释。只需使用那个并删除下面的所有内容。
  3. 删除对 module.export 的引用。这就像删除那行一样简单,因为我们只需要在这个文件中使用这个函数。

我没有精确的时间测量,但它从生成图像时的大约 10 秒延迟时间到不到一秒的延迟时间。我在这里使用“滞后时间”来表示使用页面时性能缓慢。

于 2016-01-27T23:11:25.747 回答