Canvas导出失败或模糊主因是未处理devicePixelRatio和跨域污染;需设img.crossOrigin、服务端配CORS、避免file://协议、显式设置canvas宽高并适配dpr、导出前清空画布、按格式正确处理背景与兼容性。

Canvas 图片导出失败或模糊,90% 是没处理 devicePixelRatio 或跨域污染,不是代码逻辑问题,而是浏览器强制限制和物理像素对齐问题。
toDataURL() 报 SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement'
这是典型的跨域污染(tainted canvas)错误,一旦 canvas 绘制了未授权的远程图片,浏览器立刻禁止导出。
- 检查所有
new Image()实例:是否漏写了img.crossOrigin = 'anonymous' - 服务端必须返回
Access-Control-Allow-Origin响应头(不能是*+ credentials) - 本地开发时别双击打开 HTML 文件——
file://协议下任何跨域请求都失败,必须用npx http-server启动 - 微信小程序里不能直接用
https://图片路径调drawImage,得先uni.downloadFile到tempFilePath再绘图
导出图片是黑图、空白图或内容被裁切
常见于 canvas 尺寸设置不一致:CSS 宽高 ≠ canvas.width/canvas.height,尤其在仅靠 style 缩放 canvas 时。
-
canvas.width和canvas.height必须显式设置为整数,不能依赖 CSS 或父容器尺寸自动推算 - 如果只设了
style="width: 750px; height: 1000px;",但没设canvas.width = 750,导出默认是 300×150 像素(浏览器 fallback) - 使用
getBoundingClientRect()获取尺寸后,要除以window.devicePixelRatio再赋给canvas.width,否则物理像素反而变少 - 导出前务必调用
ctx.clearRect(0, 0, canvas.width, canvas.height),避免旧帧残留叠加模糊
导出 PNG/JPEG/WebP 格式选型与参数陷阱
格式不是随便换的,alpha 通道、兼容性、体积三者强耦合,错配会导致黑底、报错或 Safari 白屏。
立即学习“前端免费学习笔记(深入)”;
- PNG:用
canvas.toDataURL('image/png'),保留透明,适合含阴影/半透图层的 UI 导出 - JPEG:用
canvas.toDataURL('image/jpeg', 0.9),第二个参数是质量(0–1),但会把透明区域填黑——若 canvas 背景是透明的,必须先ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, w, h)填白底 - WebP:用
canvas.toDataURL('image/webp', 0.8),体积比 JPEG 小 25%~30%,但 iOS 14 以下 Safari 不支持,生产环境需降级:try { ...webp... } catch { ...png... }
小程序端 uni.canvasToTempFilePath 导出模糊或尺寸错乱
这不是 uni-app 的 bug,是微信原生 Canvas API 对 destWidth/destHeight 的强依赖,漏传就按 CSS 尺寸输出,直接糊掉。
- 必须显式传
destWidth和destHeight,值为设计 rpx 尺寸 × dpr(例如 750rpx × 2 = 1500) -
width/height参数也建议传,用于指定裁剪区域;不传则默认全图,但可能因缩放导致边缘偏移 - 每次 canvas DOM 尺寸变化(比如横竖屏切换、弹窗遮挡),都要重新执行:
node.width = w * dpr、node.height = h * dpr、ctx.scale(dpr, dpr),否则第二次绘制就失效 - App 端若用
type="2d"上下文,iOS 某些版本 WebGL 不稳定,可改用type="webgl"并禁用抗锯齿:ctx.imageSmoothingEnabled = false
最易被忽略的是:devicePixelRatio 可能动态变化(插拔显示器、系统缩放调整),而多数人只在初始化时读一次 window.devicePixelRatio。真正在意清晰度的项目,得监听 matchMedia('(resolution: ...)') 或重写 resize 逻辑做兜底。



















