不是canvas“画得不够细”,而是未按devicepixelratio放大绘图缓冲区并调用ctx.scale();需在onready中获取canvas节点,设node.width/height为逻辑尺寸×dpr,再创建context并前置ctx.scale(dpr,dpr),导出时destwidth/destheight也须设为逻辑×dpr。

直接结论:不是Canvas“画得不够细”,而是你没让Canvas用足设备的物理像素——必须按devicePixelRatio放大绘图缓冲区,并在绘图前调用ctx.scale(),否则导出的图片天生缺像素。
为什么uni.canvasToTempFilePath导出的图在iPhone上糊成一片
微信小程序里Canvas默认以逻辑像素(如750rpx宽)为单位渲染,但iPhone等设备的devicePixelRatio是2或3。你画的是750×1334逻辑像素,实际物理像素只有750×1334,而屏幕需要1500×2668甚至更高才能清晰显示。系统自动插值拉伸,结果就是毛边、文字发虚、二维码扫不出。
- 现象:真机模糊,开发者工具正常(因为工具dpr=1)
- 关键线索:导出图片宽度/高度刚好等于你写的
canvas.width/canvas.height值,而不是它的dpr倍 - 错误做法:只改CSS宽高(
style="width: 750px"),不改canvas.width属性
必须在onReady里重设canvas的width和height属性
Canvas的width/height属性决定绘图缓冲区真实像素数,CSS只是控制它在页面上“看起来多大”。两者不一致,浏览器就强制缩放,画质崩坏。
- 先用
uni.getSystemInfoSync().pixelRatio拿到当前设备dpr - 用
uni.createSelectorQuery()选中canvas节点,拿到node对象 - 设置
node.width = 逻辑宽度 * dpr、node.height = 逻辑高度 * dpr(注意:不是style.width) - 之后再调用
uni.createCanvasContext(canvasId, this),否则context会基于旧尺寸初始化
ctx.scale(dpr, dpr)必须在所有绘图操作之前调用
缩放不是“画完再放大”,而是告诉Canvas:“我下面所有坐标、字号、线宽,都按逻辑尺寸写,你自动帮我映射到高分屏物理像素上”。漏掉这步,哪怕缓冲区设对了,文字也会被画小、变细、位置偏移。
- 每次清空重绘(
ctx.clearRect()后)都要重新scale,不能只设一次 - 字号、
lineWidth、坐标全部按设计稿逻辑值写(比如字体14px、x=20),不用手动乘dpr - 如果用了
drawImage加载图片,确保图片本身分辨率够高(@2x/@3x图或SVG),否则放大后还是糊
导出时destWidth/destHeight要匹配逻辑尺寸 × dpr
uni.canvasToTempFilePath的destWidth和destHeight参数,必须填你最终想要的“物理像素宽高”,也就是逻辑尺寸 × dpr。填错会导致二次压缩或裁剪失真。
- 例如设计稿是750×600 rpx,dpr=3,则
destWidth: 2250、destHeight: 1800 - 不传这两个参数?微信会按Canvas当前
width/height导出,但若你忘了重设node尺寸,它就还是750×600,导出即糊 - 体积超120KB?微信会无损压缩——这不是Canvas问题,是你要提前压缩图片资源或降色深
最易忽略的一点:Canvas节点必须在onReady钩子中获取并修改,不能在onLoad或mounted里操作;因为此时DOM未挂载完成,select查不到节点,node.width赋值无效。很多“明明写了scale却还是糊”的case,根源都在这里。











