跳到主要内容

认识

2025年02月09日
柏拉文
越努力,越幸运

一、认识


OffscreenCanvas 是一种可以在后台线程(如 Web Worker)中进行绘图操作的 API,它允许开发者在不阻塞主线程的情况下处理图形渲染任务。这对于需要大量图形处理的应用程序,如图像处理、游戏开发和数据可视化等,非常有用。

OffscreenCanvas 在主线程之外工作:OffscreenCanvas 允许你在 Web Worker 中进行图形操作,而不影响主线程的 UI 渲染。这有助于提高应用的响应性,尤其是在处理大量渲染任务时。

OffscreenCanvasHTMLCanvasElement API 类似:它支持与常规的 <canvas> 元素一样的图形绘制接口,例如 getContext()fillRect()drawImage() 等。

OffscreenCanvas 适用于 Web Worker:由于 OffscreenCanvas 的特殊设计,它可以在 Web Worker 中使用,使得图形计算和渲染任务可以在独立线程中进行,从而避免主线程被阻塞。

注意: worker.postMessage({ canvas: canvas }) 这种做法是不可行的,因为原生的 HTMLCanvasElement 不能直接传递到 Web Worker 中。原因是 HTMLCanvasElementDOM 对象,无法在 Web Worker 中进行跨线程的传递。但是,OffscreenCanvas 是专门设计用来在 Web Worker 中使用的,它可以被传递给 Web Worker,并且能够进行图形操作。因此,如果你希望在 Web Worker 中进行渲染或图形计算,你需要使用 OffscreenCanvas,而不是传统的 HTMLCanvasElement

二、语法


使用 OffscreenCanvasWeb Worker 中进行绘制操作,并将绘制结果传回主线程进行显示

2.1 主线程

主线程将 offscreenCanvas 传递给 Web Worker,并将其所有权转移到 Worker 中, 通过将 offscreenCanvas 包含在可转移对象数组中,offscreenCanvas 的所有权从主线程转移到 Worker, 后续主线程将无法再直接操作 offscreenCanvas。主线程最后接收绘制结果并显示。

const worker = new Worker("./worker.js");
const canvas = document.getElementById("canvas");
const offscreenCanvas = new OffscreenCanvas(canvas.width, canvas.height);

worker.postMessage({ canvas: offscreenCanvas }, [offscreenCanvas]);

worker.onmessage = (event) => {
const { bitmap, error } = event.data;

if (error) {
console.error("Worker error:", error);
return;
}

if (bitmap) {
const mainCanvasContext2d = canvas.getContext("2d");
mainCanvasContext2d.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
}
};

worker.onerror = (error) => {
console.error("Worker error:", error);
};

2.2 Worker

offscreenCanvas 的所有权从主线程转移到 Worker, 在 Worker 独立线程中进行绘制, 并将绘制结果传回主线程

async function draw(canvas) {
try {
const ctx2D = canvas.getContext("2d");

ctx2D.fillStyle = "blue";
ctx2D.fillRect(10, 10, 100, 100);

const bitmap = await canvas.transferToImageBitmap();
self.postMessage({ bitmap });
} catch (error) {
self.postMessage({ error: error.message });
}
}

self.onmessage = (event) => {
const { canvas } = event.data;

draw(canvas);
};