使用 OffscreenCanvas 和 Web Workers 提高图形性能

1,014 阅读3分钟

improve-performance-offscreencanvas-web-workers.png

我正在参加「掘金·启航计划」!

Canvas API 可以在网站上绘画图形、动画和其他令人兴奋的东西。但是绘制这些图形、动画的操作可能会占用大量资源并降低用户界面的响应速度。

过去执行画布操作严格在主线程上,因为它们依赖于canvas需要访问 DOM 的 HTML 元素。值得庆幸的是:技术更新迭代、突飞猛进。在本教程中,我们将研究 OffScreenCanvasWeb 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并在后台线程上运行画布操作。需要使用postMessageOffScreenCanvas实例发送给Web Worker.

可以使用OffScreenCanvastransferControlToOffscreen功能与常规画布同步。执行此操作后,在 OffScreenCanvas 上执行的操作将自动转移到原始画布元素上。

⚠️ Safari浏览器 不支持 OffscreenCanvas!

如何使用 Web Worker 设置 OffScreenCanvas

现在让我们看看如何使用OffScreenCanvasweb 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.widthstyle.height等,在这些情况下,您可以在实例中注入这些属性,OffScreenCanvas然后再将其传递给库。

结论

在这篇文章中,我们介绍了如何使用 OffScreenCanvas 来提高画布操作的性能。OffScreenCanvas 是避免其中一些问题的好方法,而且使用起来很简单。