javascript 使用 Web Workers 使用本机画布功能进行绘图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8170431/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 02:31:46  来源:igfitidea点击:

Using Web Workers for drawing using native canvas functions

javascripthtml5-canvasweb-workerdrawimage

提问by pimvdb

It's possible to send a CanvasPixelArrayobtained via getImageDatato a worker script, and let the worker script manipulate the pixels in its background thread, and eventually post the manipulated pixel array back.

可以将CanvasPixelArray获取的 via发送getImageData到工作脚本,并让工作脚本在其后台线程中操作像素,并最终将操作的像素数组发回。

However, I'm using native canvas drawing functions, like drawImage. The drawImagecalls are currently blocking the UI thread. This causes slow redraw of buttons, and a noticeable delay when clicking on a button, just to name a few drawbacks. (Edit: A small improvement can now be accomplished with ctx.imageSmoothingEnabled = false, at least on WebKit with the webkitprefix.)

但是,我使用的是本机画布绘图功能,例如drawImage. 这些drawImage调用当前正在阻塞 UI 线程。这会导致按钮的重绘缓慢,并且在单击按钮时会出现明显的延迟,仅举几个缺点。(编辑:现在可以使用ctx.imageSmoothingEnabled = false,至少在带有webkit前缀的WebKit 上完成一个小的改进。)

I'd like to move the drawing from the main thread into a background thread using Web Workers. However, I don't seem to be able to send the canvas nor the context to the worker.

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

I did find this notice on MDN:

我确实在 MDN 上找到了这个通知

Note: As usual, background threads—including workers—cannot manipulate the DOM. If actions taken by the background thread need to result in changes to the DOM, they should post messages back to their creators to do that work.

注意:像往常一样,后台线程(包括工作线程)无法操作 DOM。如果后台线程采取的操作需要导致对 DOM 的更改,则它们应该将消息发送回其创建者以完成该工作。

But I would like to leave the DOM as-is; I simply want to draw things on a canvas element. Is this possible, or are Web Workers really only allowed to calculate and not to draw?

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

(Or is it perhaps possible to use functions like drawImageto manipulate a CanvasPixelArrayinstead of drawing on a canvas?)

(或者是否可以使用类似drawImage操作 aCanvasPixelArray而不是在画布上绘图的功能?)

采纳答案by John Watson

[community edit: This answer was written and accepted in 2011. Other technologies have emerged (or are emerging) which may allow Web Workers and Canvas to co-exist better; the reader should be aware of all the answers on this page besides this answer.]

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

You cannot pass a canvas object or canvas context to a worker thread because the canvas is part of the DOM.

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

回答by AshleysBrain

[Edit ~5 years later: some of this is beginning to change, and there are new web platform features that actually allow rendering to a canvas from a Worker! See this blog for more info: https://hacks.mozilla.org/2016/01/webgl-off-the-main-thread/- the rest of the answer is provided for 2011-era information ;)]

[编辑 ~ 5 年后:其中一些开始发生变化,并且有新的网络平台功能实际上允许从 Worker 渲染到画布!有关更多信息,请参阅此博客:https: //hacks.mozilla.org/2016/01/webgl-off-the-main-thread/- 其余答案提供给 2011 时代的信息;)]

Web workers can only calculate, not modify the DOM or make any calls to draw to a canvas. However like you say you can post an array of pixels to a web worker to process it there and post it back. Since that's asynchronous I don't see why that would cause any slowdown in the UI thread, unless you deliberately block until the web worker responds (which you shouldn't).

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

So it seems odd that your drawImagecalls are taking so long it's affecting the UI. Most canvases are hardware-accelerated these days so they should skip along nicely. My guess is that you're drawing via a web worker to a canvas pixel array every frame, which effectively means you're software rendering the canvas in javascript. Javascript is still far too slow to do this - even C++ software renderers are kind of slow, which is why hardware acceleration is important. So you can render something to a canvas pixel array in a web worker once, and then when you get your result cache it in to an Imageonce, and then draw that Imageto the canvas as much as you like. That should still be really fast.

因此,您的drawImage呼叫花费的时间如此之长以至于影响了 UI ,这似乎很奇怪。现在大多数画布都是硬件加速的,所以它们应该很好地跳过。我的猜测是,您每帧都通过网络工作者将画布绘制到画布像素数组,这实际上意味着您正在使用 javascript软件渲染画布。Javascript 仍然太慢,无法做到这一点——即使是 C++ 软件渲染器也有点慢,这就是硬件加速很重要的原因。因此,您可以将某些内容渲染到 Web Worker 中的画布像素数组一次,然后在获得结果时将其缓存到Image一次,然后根据Image需要将其绘制到画布上。那应该仍然非常快。

Edit: you might want to look in to WebGL, where you can write fragment shaders which are effectively little programs to process pixel effects. They run entirely on the graphics card so they're stupidly fast.

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

回答by Zsolt

You can post the ImageDatato the Web Worker, which sends the manipulated ImageDataback to the caller (Main UI) thread.

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

E.g.:

例如:

  1. Create a Web Worker:

    this.renderer = new Worker("renderer.js");
  2. Post the ImageDatacreated from the canvas to the Web Worker:

    var ctx = this.canvas.getContext('2d');
    var imageData = ctx.createImageData(width, height);
    this.renderer.postMessage({ image: imageData });
  3. Do ImageDatamanipulation in Web Worker and post it back to Main thread:

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

  4. Set the manipulated ImageDatato the canvas in the Main thread:

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

  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. ImageData操纵Web工作,并张贴回主线程:

    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);
    }

回答by Marco

There's a new API to do this (currently only supported in Firefox if you enable a pref).

有一个新的 API 可以做到这一点(如果您启用了首选项,目前仅在 Firefox 中受支持)。

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

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