我正在参加「掘金·启航计划」!
Canvas API
可以在网站上绘画图形、动画和其他令人兴奋的东西。但是绘制这些图形、动画的操作可能会占用大量资源并降低用户界面的响应速度。
过去执行画布操作严格在主线程上,因为它们依赖于canvas
需要访问 DOM 的 HTML 元素。值得庆幸的是:技术更新迭代、突飞猛进。在本教程中,我们将研究 OffScreenCanvas
和 Web Workers
如何帮助我们提高画布性能。
Web Workers API
Web Worker
为 Web 内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面。
Web worker
允许在后台运行 JavaScript 代码,与主 UI 线程分开。可以使用它们来提高应用程序的性能并运行任务。
主线程和 web worker 可以通过设置事件处理程序和使用postMessage
方法发布消息来进行通信。
OffscreenCanvas
Web Workers
非常棒,但对浏览器 API 的访问权限有限。例如,不能直接从 Web worker
中操作 DOM 元素——它只能从主线程中操作。
值得庆幸的是,这就是需要OffScreenCanvas
的时候!OffscreenCanvas
界面是一个可以在屏幕外呈现的画布,将其与 DOM 分开。
OffScreenCanvas
是一个可传输对象,这意味着可以安全地将它传递给Web Workers
并在后台线程上运行画布操作。需要使用postMessage
将OffScreenCanvas
实例发送给Web Worker
.
可以使用OffScreenCanvas
的transferControlToOffscreen
功能与常规画布同步。执行此操作后,在 OffScreenCanvas
上执行的操作将自动转移到原始画布元素上。
⚠️ Safari浏览器
不支持 OffscreenCanvas
!
如何使用 Web Worker 设置 OffScreenCanvas
现在让我们看看如何使用OffScreenCanvas
和web workers
来提高性能。
我们的项目将有一个启动动画的文件index.js
:
function runAnimation() {
const canvas = document.getElementById("myCanvas").transferControlToOffscreen();
const worker = new Worker("script.js");
worker.postMessage({ canvas }, [canvas]);
worker.postMessage({});
}
runAnimation();
<canvas/>
非常简单:加载脚本时,它会获取对文件中元素的引用,并使用该函数创建 OffScreenCanvas
的实例transferControlToOffscreen
。
如代码所示,它还启动了我们的 worker 并对其调用了postMessage
两次:
- 第一次将 OffScreenCanvas 实例传输给 worker
- 第二次触发动画
现在让我们看看 worker.js
是什么样子的:
let canvas;
function animate(ctx) {
ctx.beginPath();
ctx.moveTo(100, 0);
ctx.lineTo(100, 200);
ctx.stroke();
function drawCircle(x) {
ctx.beginPath();
ctx.arc(x, 100, 10, 0, 2 * Math.PI);
ctx.fillStyle = "red";
ctx.fill();
}
let x = 0;
setInterval(function () {
ctx.clearRect(0, 0, 600, 600);
drawCircle(x % 200);
x++;
}, 25);
}
onmessage = function (e) {
console.log("Worker received a message from the main thread");
const { canvas: canvasMessage } = e.data;
if (canvasMessage) {
canvas = canvasMessage;
} else {
const context = canvas.getContext("2d");
animate(context);
}
};
我们的js脚本使用该函数设置一个监听器onmessage
。
当我们的 worker 从主线程接收到消息时,它会分配canvasMessage
给它的全局canvas
变量。
第二次收到消息时,它会调用animate
运行画布动画的函数。
现在,即使我们的主线程变得繁忙且响应速度变慢,也不会影响我们的 worker 的执行。它仍然会运行动画并更新画布。反之亦然,在 Web Worker 中运行的动画不会阻止 UI 线程平滑地响应用户输入。
与其他库一起使用
另一个好处OffScreenCanvas
是你可以用它来提高依赖画布的第三方图形库的性能,比如 three.js 。由于OffscreenCanvas API
通常与常规元素canvas
兼容。
但需要注意的一件事是 OffScreenCanvas
不具有其中一些库可能需要的属性:style.width
。style.height
等,在这些情况下,您可以在实例中注入这些属性,OffScreenCanvas
然后再将其传递给库。
结论
在这篇文章中,我们介绍了如何使用 OffScreenCanvas
来提高画布操作的性能。OffScreenCanvas 是避免其中一些问题的好方法,而且使用起来很简单。