在 Webworker 中绘制 canvas
transferControlToOffscreen 实现 Webworker 绘制
在 Webworker 中绘制 canvas
使用 transferControlToOffscreen 将离屏 canvas 副本传给 Webworker,在 Webworker 中实现绘制。
页面代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>canvas.transferControlToOffscreen</title>
<style>
.caption {
font-weight:bold;
text-align: center;
margin-bottom:1rem;
}
#myCanvas {
border:1px solid gray;
padding:1rem;
border-radius: 5px;
display: block;
margin:0 auto;
}
</style>
</head>
<body>
<div class="caption">
Canvas transferControlToOffscreen demo
</div>
<canvas id="myCanvas" width=800 height=600>
canvas demo
</canvas>
<script>
function drawSomething() {
console.time('绘制用时')
const canvas = document.getElementById("myCanvas");
const offScreenCanvas = canvas.transferControlToOffscreen();
const worker = new Worker(`./worker.js?ts=${new Date().getTime()}`);
// 如果是内嵌 worker 代码,需要使用如下方式使用:
// const blob = new Blob([document.querySelector('#worker').textContent]);
// const url = window.URL.createObjectURL(blob);
// const worker = new Worker(url);
worker.onmessage = msg => {
console.log(msg.data);
console.timeEnd('绘制用时');
}
// postMessage 传递 Transferable Objects 需要用到第二个参数
worker.postMessage({canvas:offScreenCanvas},[offScreenCanvas]);
}
window.onload = ()=> drawSomething();
</script>
</body>
</html>
Webworker 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
const delay = (n=50) => new Promise(resolve=>setTimeout(resolve,n));
const randomColor = ()=> '#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0');
const rndFromRange = (a,b) => Number.parseInt(a+Math.random()*(b-a))
const requestAnimationFramePromise = cb =>{
return new Promise((resolve,reject)=>{
const cbType = Object.prototype.toString.call(cb).slice(8,-1);
// 参数 cb 不是函数或异步函数
if(!['Function','AsyncFunction'].includes(cbType))
reject();
requestAnimationFrame(async ()=>{
let rt = null;
try {
rt = await cb();
await delay();
} catch(e) {
reject(e)
}
resolve(rt);
})
});
}
// 需将 this 绑定到 ctx
const drawCircle = async function (_cx=0,_cy=0,_r=10,color='#00000') {
this.save()
this.beginPath();
this.arc(_cx,_cy,_r,0,2*Math.PI);
this.fillStyle = this.strokeStyle = color;
this.stroke();
this.fill();
this.restore()
await delay();
}
onmessage = async(message) =>{
const promises = [];
const canvas = message.data.canvas;
const ctx = canvas.getContext("2d");
console.log(`canvas.width=${canvas.width} canvas.height=${canvas.height} `);
for(let n=0;n<100;n++) {
const r = Number.parseInt(Math.random()*Math.min(canvas.width,canvas.height)/8)
const cx = rndFromRange(r,Number.parseInt(canvas.width-r))
const cy = rndFromRange(r,Number.parseInt(canvas.height-r))
const color = randomColor()
console.log(`[${n}] (${cx},${cy}),r=${r},color=${color}`)
const p = requestAnimationFramePromise(drawCircle.bind(ctx,cx,cy,r,color));
promises.push(p);
}
Promise.all(promises).then(()=>{
postMessage('绘图全部完成!');
}).catch(e =>console.log(e))
}
参考资料
本文由作者按照 CC BY 4.0 进行授权