前言
前段时间有在开发小程序的图片处理需求,用到了wx.createOffscreenCanvas,可以直接创建canvas实例来进行画布的绘制,我当时很惊讶,好奇 Web 没有这个能力吗,因为小程序的 Canvas 标准是根据 Web 标准升级来的,于是我在MDN查了一下,发现了这个能力,但是当前属于实验阶段。(不能上生产)
在 Web 中使用 OffscreenCanvas
这个能力主要是为了将耗时渲染移出主线程,所以并没有完全像小程序一样,直接通过wx.createOffscreenCanvas来完全不使用<Canvas>来进行画布绘制,搓了一个示例代码
<!DOCTYPE html>
<html>
<body>
<canvas id="canvas" width="800" height="600"></canvas>
</body>
<script>
// 主线程创建 Canvas 实例
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('bitmaprenderer');
// 创建 OffscreenCanvas 实例
const offscreenCanvas = new OffscreenCanvas(800, 600);
// 在 Worker 线程中绘制图形
const worker = new Worker('./work.js');
worker.postMessage({ canvas: offscreenCanvas }, [offscreenCanvas]);
// 将 OffscreenCanvas 的渲染结果绘制到 Canvas 上
worker.onmessage = function (e) {
ctx.transferFromImageBitmap(e.data.imageBitmap);
};
</script>
</html>
// ./work.js
onmessage = function (e) {
const canvas = e.data.canvas;
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 100, 100);
const imageBitmap = canvas.transferToImageBitmap();
postMessage({ imageBitmap });
};
- 在主线程中根据
canvas元素创建了一个画布实例,同时创建了一个offscreenCanvas离屏实例,同时向work线程中传入我的offscreenCanvas实例。 - 同时
worker线程接收到实例后执行我的绘制操作,再将imageBitmap传到主线程,主线程的canvas实例通过transferFromImageBitmap将内容填充到画布上,完成整个绘制流程
这里你可能会有几个疑问:
getContext('bitmaprenderer');的参数是bitmaprenderer- 为什么要这么麻烦,将绘制流程转到
worker线程中
接下来我继续给大家介绍
大家对canvas.getContext这个函数的参数可能对2d与webgl比较熟悉,如果传入bitmaprenderer,那么将创建一个只可以传入ImageBitmap类型内容的画布上下文。
而OffscreenCanvas的transferToImageBitmap返回的内容为ImageBitmap,所以结合getContext('bitmaprenderer')使用,也当然按需要也可以使用OffscreenCanvas.convertToBlob(),这个转换能力是异步的,所以可以:
onmessage = function (e) {
const canvas = e.data.canvas;
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 100, 100);
const imageBitmap = canvas.transferToImageBitmap();
canvas.convertToBlob().then((blob) => {
postMessage({ imageBitmap, blob });
});
};
在拿到图像的blob后一同传入主线程,如果有对图像blob内容的需要,在这里即可完成转换,如果有即时绘制的要求也可以使用ImageBitmap.
web worker
我们放在worker内的代码将会放在单独的线程内,也就是我们可以在里面执行需要放在后台去执行的内容,来减少主线程的压力。
主线程和worker线程使用postmessage进行通信,比如上文的代码,那为什么我非要使用OffscreenCanvas来进行画布的处理呢?
因为worker线程有一个需要注意的事情,那就是无法在worker内操作DOM元素,所以我们需要借助OffscreenCanvas的能力结合worker来实现真正的离屏绘制。
最后
目前这个能力还处在实验阶段,相信过不了多久我们就可以使用这个能力,来优化我们的绘制性能。