不能直接用 blob 在 worker 中静默生成业务海报,因 blob 非可绘制图像,必须由主线程预解码为 imagebitmap 并零拷贝传递;worker 仅能执行确定性像素操作,文字布局、字体测量、最终下载均需主线程协作。

不能直接用 Blob 在 Worker 里“静默生成业务海报”,关键在于 Blob 本身不是可绘制图像,必须先解码为 ImageBitmap 才能被 OffscreenCanvas 使用;而解码操作(createImageBitmap(blob))虽可在 Worker 中执行,但若原始图片未预加载或跨域受限,仍会失败或退化回主线程。真正可行的路径是:主线程完成资源准备与初始渲染,Worker 仅做纯像素级合成。
主线程必须提前准备好 ImageBitmap
OffscreenCanvas 在 Worker 中不支持 new Image()、fetch().then(res => res.blob()) 或直接传 <img> 元素。所有图像输入必须是转移able类型——最可靠的是 ImageBitmap,且需由主线程调用 createImageBitmap(blob) 创建后,通过 postMessage(data, [bitmap]) 零拷贝传递。
- 避免在 Worker 里调用
createImageBitmap()处理网络 Blob:它可能触发隐式主线程调度,尤其在跨域或字体未就绪时 - 推荐做法:主线程用
fetch('poster-bg.png').then(r => r.blob()).then(createImageBitmap),再worker.postMessage({ type: 'init', bg: bitmap }, [bitmap]) - 如需多图(头像、logo、二维码),全部在主线程解码并 transfer,不要只传 URL 让 Worker 自己 fetch
Worker 中只做确定性图像操作
OffscreenCanvas 在 Worker 里没有 DOM、没有样式、不能测量文字宽高、也不能自动换行。所谓“高分业务海报”,其布局、字体、尺寸都必须由主线程计算好并传入。
- 文字绘制前,主线程应调用
ctx.measureText(text)并把 x/y/width 一并传给 Worker;Worker 仅执行ctx.fillText(text, x, y) - 所有缩放、旋转、滤镜、水印叠加,都用
ctx.drawImage(bitmap, sx, sy, sw, sh, dx, dy, dw, dh)显式控制,不依赖 CSS 或 layout - 避免任何
getImageData()、toDataURL()或transferToImageBitmap()—— 这些会强制同步回主线程,破坏“静默”和“独立线程”前提
输出结果要回到主线程才能下载
Worker 内无法触发文件下载,也不能访问 a.download。最终图像必须导出为 Blob 并交还主线程处理。
- Worker 完成绘制后,调用
offscreen.convertToBlob({ type: 'image/png', quality: 0.95 })得到 Blob - 用
postMessage({ type: 'done', blob }, [blob])发送回主线程(注意加入 transfer 列表) - 主线程收到后创建 URL:
const url = URL.createObjectURL(blob),再用<a href="%24%7Burl%7D" download="business-poster.png"></a>触发下载 - 别忘了后续调用
URL.revokeObjectURL(url)防止内存泄漏
为什么不能完全“静默”?这些细节容易踩坑
所谓“静默”是指用户无感知、不卡顿、不弹窗,但技术上仍需少量主线程协作。常见失效点包括:
- 主线程未等字体加载完成就拍快照 → 海报文字显示为方块(可用
document.fonts.load('bold 16px "PingFang SC"')确保) - Worker 每次都 new OffscreenCanvas(1242, 2208) → GC 压力反向拖慢主线程;建议复用单个 OffscreenCanvas 实例
- 传入的 ImageBitmap 尺寸与 canvas 不匹配,又没设
imageSmoothingEnabled = false→ 渲染模糊,影响“高分”效果 - 使用
toDataURL()替代convertToBlob()→ Base64 编码吃 CPU,大图易卡死,且无法 transfer











