使用Web Workers使用本机canvas函数进行绘制

时间:2011-11-17 16:11:36

标签: javascript html5-canvas web-worker drawimage

可以将CanvasPixelArray通过getImageData发送到工作人员脚本,然后让工作人员脚本操纵其后台线程中的像素,最后将操纵的像素数组发回。

但是,我使用的是原生画布绘图功能,例如drawImagedrawImage调用当前正在阻止UI线程。这会导致按钮重绘速度慢,单击按钮时会出现明显的延迟,这只是为了说明一些缺点。 (编辑:现在可以使用ctx.imageSmoothingEnabled = false完成一项小改进,至少在带有webkit前缀的WebKit上完成。)

我想使用Web Workers将绘图从主线程移动到后台线程中。但是,我似乎无法将画布和上下文发送给工作人员。

我找到了this notice on MDN

  

注意:像往常一样,后台线程(包括worker)无法操纵DOM。如果后台线程采取的操作需要对DOM进行更改,则应将消息发送回创建者以执行该操作。

但我想按原样保留DOM;我只是想在canvas元素上绘制东西。这是可能的,还是Web Workers真的只允许计算而不是绘制?

(或者也许可以使用像drawImage这样的函数来操纵CanvasPixelArray而不是在画布上绘图?)

4 个答案:

答案 0 :(得分:36)

[编辑〜5年后:其中一些已经开始改变,并且有新的网络平台功能实际上允许从工作者渲染到画布!有关详细信息,请参阅此博客:https://hacks.mozilla.org/2016/01/webgl-off-the-main-thread/ - 其余答案均提供2011年时代的信息;)

Web worker只能计算,不能修改DOM或进行任何调用以绘制到画布。但是就像你说的那样,你可以向网络工作者发布一个像素数组,以便在那里进行处理并将其发回。由于这是异步的,我不明白为什么会导致UI线程的任何减速,除非你故意阻止,直到web工作者响应(你不应该)。

所以你的drawImage电话花了很长时间才会影响用户界面,这似乎很奇怪。这些天大多数画布都是硬件加速的,所以它们应该很好地跳过。我的猜测是你通过网络工作者绘制到画布像素数组每一帧,这实际上意味着你是在javascript 中渲染画布的软件。 Javascript仍然太慢了 - 即使C ++软件渲染器也很慢,这就是硬件加速很重要的原因。因此,您可以将某些内容渲染到Web worker 中的画布像素数组,然后当您将结果缓存到Image 一次时,然后根据需要将Image绘制到画布上。那应该还是非常快。

编辑:你可能想要查看WebGL,在那里你可以编写片段着色器,它实际上是处理像素效果的小程序。它们完全在显卡上运行,因此速度非常快。

答案 1 :(得分:12)

[社区编辑:这个答案是在2011年编写并被接受的。其他技术已经出现(或者正在出现),可以让Web Workers和Canvas更好地共存;除了这个答案,读者应该知道这个页面上的所有答案。]

您无法将画布对象或画布上下文传递给工作线程,因为画布是DOM的一部分。

答案 2 :(得分:10)

您可以将ImageData发布到Web Worker,后者将被操纵的ImageData发送回调用者(主UI)线程。

E.g:

  1. 创建Web Worker:

    this.renderer = new Worker("renderer.js");
  2. 将从画布创建的ImageData发布到Web Worker:

    var ctx = this.canvas.getContext('2d');
    var imageData = ctx.createImageData(width, height);
    this.renderer.postMessage({ image: imageData });
  3. 在Web Worker中进行ImageData操作并将其发布回主线程:

    onmessage = function(e) {
       var processedImage = self.doImageProcessing(e.data.image);
       postMessage({ image: processedImage });
    };

  4. 将操纵的ImageData设置为主线程中的画布:

    this.renderer.onmessage = function (e) {
       var ctx = this.canvas.getContext('2d');
       ctx.putImageData(e.data.image, 0, 0);
    }

答案 3 :(得分:5)

有一个新的API可以执行此操作(目前仅在Firefox中支持,如果您启用了pref)。

https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvashttps://hacks.mozilla.org/2016/01/webgl-off-the-main-thread/