我正在参加「掘金·启航计划」!
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 是避免其中一些问题的好方法,而且使用起来很简单。