canvasToTempFilePath失败主因是绘图未完成就导出,需用ctx.draw(false,callback)确保GPU绘制完毕;iOS高度超4096px会空白,应缩放或分段;文字模糊因未适配dpr,须设置canvas物理宽高并scale;H5跨域污染需服务端代理或改用CSS/SVG。

canvasToTempFilePath: fail canvas is empty 怎么破
这不是 canvas 没画,而是 canvasToTempFilePath 执行得太早——绘图还没真正提交到 GPU 缓冲区就去导出,结果拿到空图。微信小程序真机(尤其 iOS)对渲染时序极其敏感,ctx.draw() 的回调只表示“CPU 指令已下发”,不保证“GPU 已完成绘制”。
实操建议:
- 必须用
ctx.draw(false, callback),且callback里再调canvasToTempFilePath,不能靠await或setTimeout(100)猜时机 - Vue 3 中
this不可用,别传错上下文;应明确传入组件实例(如uni.createCanvasContext('id', instance)) - 若用了
type="2d",确保 canvas 节点已挂载:用uni.createSelectorQuery().select('#id').fields({ node: true })拿到node后再初始化 ctx
iOS 上 canvas 高度超 4096px 直接空白怎么办
iOS 微信客户端对 canvas 渲染缓冲区有硬限制:高度超过 4096 物理像素时,绘图命令会被静默丢弃,不报错、不警告、只留白。这不是 bug,是 WebKit 内核的内存保护策略。
关键判断点:海报在开发者工具和安卓机正常,iPhone 上整块空白 → 先查逻辑高度 × dpr 是否 > 4096。
解决路径:
- 动态缩放:若原始设计稿高 2000rpx,dpr=3 时物理高度达 6000px,此时需将目标逻辑高度压缩到 ≤1365rpx(4096 ÷ 3 ≈ 1365)
- 分段绘制:把长图拆成多个
分别生成,再用后端拼接(适合服务端可控场景) - 绕过 canvas:改用
painter插件(基于 WXML 节点快照),支持 rpx 计算且无高度限制,但不支持复杂滤镜或动态文字测量
为什么海报文字模糊、边缘发虚
根本不是 canvas 画质低,是没处理 devicePixelRatio(dpr)。iPhone 14 的 dpr 是 3,你按 375×667 的逻辑尺寸设置 canvas 宽高,实际缓冲区只有 375×667 像素,被拉伸到 1125×2001 物理像素,必然糊。
正确做法分三步:
- 取真实 dpr:
const dpr = uni.getSystemInfoSync().pixelRatio - 设 canvas 属性宽高(非 style):
node.width = 375 * dpr; node.height = 667 * dpr - 绘图前缩放 ctx:
ctx.scale(dpr, dpr),之后所有坐标/字号/线宽都按 375×667 写,不用乘 dpr
漏掉任意一步,高清就是空谈。
H5 端 canvas 绘制网络图后 toDataURL 报 tainted canvas
H5 和小程序行为完全不同:小程序里 uni.downloadFile + drawImage 可绕过跨域;H5 则严格遵循 CORS —— 即使图片能显示在页面上,一旦 draw 到 canvas 就污染,toDataURL 必报错。
不要试图用 img.crossOrigin = 'anonymous' 硬扛,除非你能控制 CDN 返回 Access-Control-Allow-Origin: *。
更稳的方案:
- 条件编译隔离逻辑:
#ifdef H5下走fetch+createObjectURL+drawImage路径 - 服务端代理:把图片 URL 改成自己域名下的接口,由后端加 header 后透传
- 直接放弃 H5 端 canvas 生成海报,改用 CSS + SVG 方案(适合静态内容)
最易忽略的一点:iOS Safari 对 createObjectURL 有内存限制,大图(>3MB)可能触发静默失败,务必加 try/catch 并 fallback。











