微信小程序分享海报总生成空白:我用离屏 Canvas、图片预下载和字体加载补齐失败链路
小程序海报空白多半不是绘制语法错,而是 Canvas 初始化、图片下载、字体加载和导出时序没对齐。把资源准备前置,并加上重试与降级,流程会稳定很多。

作者
ZHOUYI
做小程序分享海报时,最怕的不是报错,而是导出图一片空白。后来我发现,多数空白不是绘制代码错了,而是时序没对齐:Canvas 还没初始化完就开始画,图片还在下载,字体没加载完,最后导出了一张没有底图的图。我把链路拆成资源准备、绘制、导出三段,才慢慢稳定下来。
先把失败链路画出来
- 离屏 Canvas 是否创建成功,宽高是否显式设置,避免尺寸为 0
- 网络图片是否先 downloadFile 成临时路径,再进入 Image onload
- 远程字体是否加载完成,失败时是否回退系统字体
- 导出是否放在下一帧,避免绘制还没完成就生成图片
还有一个容易被忽略的点:下载得到的临时路径并非永久可用。文件被清理、页面重新进入、缓存过期,都可能让上次能用的路径这次画不出来。所以我每次生成海报都会重新走一遍下载和 onload 校验,而不是把旧路径直接塞给 Canvas。
资源准备好再绘制
我现在不会在绘制时直接写网络 URL,而是先用 wx.downloadFile 把背景图、头像、二维码下载到临时路径,再创建 Image。每张图片都包成 Promise,只有全部 onload 后才允许进入绘制。字体也一样:如果业务需要特殊字体,就用 wx.loadFontFace 异步加载,并设置超时;加载失败不阻塞流程,直接回退默认字体。具体能力与参数差异,以官方文档为准。
const loadImage = (canvas, src) => new Promise((resolve, reject) => {
const img = canvas.createImage();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = src;
});
async function prepare(canvas, localPaths) {
return Promise.all(localPaths.map((p) => loadImage(canvas, p)));
}导出加重试和降级
绘制完成后,我不再立即导出,而是等下一帧再调用 wx.canvasToTempFilePath。如果失败,会按重下载图片、重绘、降低清晰度导出的顺序重试一次。离屏 Canvas 的好处是不容易被页面显示和滚动影响,但实例、宽高和导出参数都要传对。海报生成这种强依赖外部资源的功能,不能假设网络稳定;把每一步做成可检查、可重试、可降级,空白问题会少很多。