使用 Web Workers 使用本机画布函数进行绘图
可以将通过 getImageData
获取的 CanvasPixelArray
发送到工作脚本,并让工作脚本在其后台线程中操作像素,并最终将操作后的像素数组发回。
但是,我使用的是本机画布绘图函数,例如 drawImage
。 drawImage
调用当前正在阻塞 UI 线程。这会导致按钮重绘缓慢,并且单击按钮时会出现明显的延迟,仅举几个缺点。 (编辑:现在可以使用 ctx.imageSmoothingEnabled = false 来完成一个小改进,至少在带有 webkit
前缀的 WebKit 上是这样。)
我想将绘图从使用 Web Workers 将主线程转换为后台线程。但是,我似乎无法将画布或上下文发送给工作人员。
我确实在 MDN 上找到了此通知:
注意:像往常一样,后台线程(包括工作线程)无法操作 DOM。如果后台线程采取的操作需要导致 DOM 发生更改,它们应该将消息发布回其创建者来完成这项工作。
但我想保持 DOM 不变;我只是想在画布元素上绘制东西。这可能吗,还是Web Workers真的只允许计算而不允许绘图?
(或者是否可以使用诸如drawImage
之类的函数来操作CanvasPixelArray
而不是在画布上绘图?)
It's possible to send a CanvasPixelArray
obtained via getImageData
to a worker script, and let the worker script manipulate the pixels in its background thread, and eventually post the manipulated pixel array back.
However, I'm using native canvas drawing functions, like drawImage
. The drawImage
calls 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 webkit
prefix.)
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.
I did find this notice on 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.
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?
(Or is it perhaps possible to use functions like drawImage
to manipulate a CanvasPixelArray
instead of drawing on a canvas?)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
[大约 5 年后编辑:其中一些开始发生变化,并且有新的 Web 平台功能实际上允许从 Worker 渲染到画布!有关更多信息,请参阅此博客:https://hacks.mozilla.org/2016/ 01/webgl-off-the-main-thread/ - 其余答案是为 2011 时代的信息提供的;)]
Web 工作人员只能计算,不能修改 DOM 或进行任何调用来绘制 帆布。然而,就像你说的,你可以将像素数组发布给网络工作人员以在那里处理它并将其发回。由于这是异步的,我不明白为什么这会导致 UI 线程变慢,除非您故意阻塞直到 Web Worker 响应(您不应该这么做)。
因此,您的
drawImage
调用花费了如此长的时间以至于影响了 UI,这似乎很奇怪。如今,大多数画布都是硬件加速的,因此它们应该可以很好地跳跃。我的猜测是,您正在通过 Web Worker 绘制画布像素数组每一帧,这实际上意味着您正在使用软件在 JavaScript 中渲染画布。 Javascript 仍然太慢,无法做到这一点 - 即使 C++ 软件渲染器也有点慢,这就是硬件加速很重要的原因。因此,您可以将某些内容渲染到网络工作者中的画布像素数组一次,然后当您获得结果时将其缓存到图像
一次 >,然后根据需要将该Image
绘制到画布上。那应该还是很快的。编辑:您可能需要查看 WebGL,您可以在其中编写片段着色器,这些片段着色器是处理像素效果的有效小程序。它们完全在显卡上运行,所以速度快得离谱。
[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 ;)]
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).
So it seems odd that your
drawImage
calls 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 anImage
once, and then draw thatImage
to the canvas as much as you like. That should still be really fast.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.
您可以将
ImageData
发布到 Web Worker,后者将处理后的ImageData
发送回调用者(主 UI)线程。例如:
创建一个 Web Worker:
将从画布创建的
ImageData
发布到 Web Worker:Do
ImageData
操作Web Worker 并将其发布回主线程:将操作的
ImageData
设置到主线程中的画布:You can post the
ImageData
to the Web Worker, which sends the manipulatedImageData
back to the caller (Main UI) thread.E.g.:
Create a Web Worker:
Post the
ImageData
created from the canvas to the Web Worker:Do
ImageData
manipulation in Web Worker and post it back to Main thread:Set the manipulated
ImageData
to the canvas in the Main thread:有一个新的 API 可以执行此操作(当前仅在启用首选项的情况下在 Firefox 中受支持)。
请参阅 https://developer.mozilla.org/en-US/docs/ Web/API/OffscreenCanvas 和
https://hacks.mozilla.org/2016/01/ webgl-off-the-main-thread/.
There's a new API to do this (currently only supported in Firefox if you enable a pref).
See https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas and
https://hacks.mozilla.org/2016/01/webgl-off-the-main-thread/.
[社区编辑:这个答案是在 2011 年编写并接受的。其他技术已经出现(或正在出现),可以让 Web Workers 和 Canvas 更好地共存;除了这个答案之外,读者还应该了解本页上的所有答案。]
您无法将画布对象或画布上下文传递给工作线程,因为画布是 DOM 的一部分。
[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.]
You cannot pass a canvas object or canvas context to a worker thread because the canvas is part of the DOM.