Web前端技术 · 第5课17 / 19 | 编码:L05-90
进阶观察 · 从本课知识向前一步

先在另一块画布上画,再合成

点击生成海报:同一块离屏图像绘制三次,位置和缩放各不相同。

先看效果

单独打开演示

01 · 离屏是什么意思

const tile = new OffscreenCanvas(180, 100);

这块画布不直接作为页面元素显示,但可以取得 2D 上下文并绘制。

02 · 先准备图层

const brush = tile.getContext('2d');
brush.fillRect(0, 0, 180, 100);

brush 与 ctx 分别属于两块画布,状态不会互相替代。

03 · 合成到可见画布

ctx.drawImage(tile, 230, 60, 144, 80);

drawImage 不只接收图片,也能接收另一块画布;这里指定目标位置与显示大小。

思考

把画布改为 OffscreenCanvas 后,绘制就一定在后台线程运行吗?三次 drawImage 会自动建立三个 HTML 元素吗?

展开参考分析

都不会。本例仍在主线程执行;要把工作移到 Worker,需要另行组织线程与消息。三次绘制只是向同一可见画布写入像素,没有创建三个可独立点击的 DOM 元素。

现代绘图路径:OffscreenCanvas 可与 Worker 配合;本例仅展示离屏复用,不宣称已经实现多线程加速。

完整源码:展开、修改与运行
<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>先在另一块画布上画,再合成</title>
  <style>
body { margin:16px; color:#183347; background:white; font:20px/1.5 "Times New Roman",SimSun,serif; }
h1,h2 { font-family:"Times New Roman",SimHei,sans-serif; }
button,input,select { font:inherit; max-width:100%; }
button { margin:4px 0; cursor:pointer; }
pre { white-space:pre-wrap; overflow-wrap:anywhere; font:18px/1.5 Consolas,SimSun,monospace; }
:focus-visible { outline:3px solid #b56220; outline-offset:3px; }
canvas { display:block; width:100%; max-width:640px; border:1px solid #abc3c5; margin-top:12px; }
  </style>
</head>
<body>
<button id="draw" type="button">生成海报</button>
<canvas id="poster" width="640" height="300">三张黄梅戏活动卡片。</canvas>
<p id="state" role="status">
</p>
  <script>
const canvas = document.querySelector('#poster');
const ctx = canvas.getContext('2d');
document.querySelector('#draw').addEventListener('click', () => {
  const supported = typeof OffscreenCanvas === 'function';
  const tile = supported ? new OffscreenCanvas(180, 100) : document.createElement('canvas');
  tile.width = 180; tile.height = 100;
  const brush = tile.getContext('2d');
  brush.fillStyle = '#166b60'; brush.fillRect(0, 0, 180, 100);
  brush.fillStyle = 'white'; brush.font = '24px SimSun';
  brush.fillText('黄梅戏', 20, 56);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.drawImage(tile, 20, 20);
  ctx.drawImage(tile, 230, 60, 144, 80);
  ctx.drawImage(tile, 410, 100, 216, 120);
  document.querySelector('#state').textContent = supported ? '使用 OffscreenCanvas 合成' : '使用普通离屏 canvas 后备方案';
});
  </script>
</body>
</html>