canvas 高精度绘图模糊的根本原因是逻辑像素与物理像素未对齐,核心在于正确处理 window.devicepixelratio(dpr):动态获取 dpr 值,按 getboundingclientrect() 获取 css 尺寸并乘以 dpr 设置 canvas.width/height,再用 canvas.style.width/height 回设 css 值,最后调用 ctx.scale(dpr, dpr)。

Canvas 在高精度绘图场景下出现模糊、线条发虚、文字毛刺,根本原因不是代码写得不够精细,而是逻辑像素和物理像素没对齐。关键变量就是 window.devicePixelRatio(简称 dpr)——它告诉你“1 个 CSS 像素对应几个真实物理像素”。
比如 iPhone 14 Pro 的 dpr 是 3,MacBook Pro Retina 屏通常是 2。如果你只按 CSS 尺寸设 canvas.width = 400,那它内部只分配了 400 个水平物理像素;但屏幕实际要显示 400px 宽度,就得用 1200 个物理像素来撑开——浏览器只能插值拉伸,结果就是糊。
真正有效的处理,是三步同步到位:
动态读取并确认 dpr 值
别硬编码 dpr = 2。真实值可能是 1、1.25(Windows 缩放)、1.5、2、3,甚至更高:
- 页面加载后立即执行
console.log(window.devicePixelRatio)看实际值 - 注意旧环境(如 IE)可能不支持,需 fallback 到
1 - 某些 WebView 可能始终返回
1,这时可结合screen.availWidth / window.innerWidth估算,但仅作备用
同步设置 canvas 的内外尺寸与坐标系
顺序不能错,缺一不可:
- 用
canvas.getBoundingClientRect()获取当前 CSS 占位宽高(响应式必备) - 设
canvas.width = Math.floor(cssWidth * dpr),canvas.height = Math.floor(cssHeight * dpr)(Math.floor防止小数导致 WebGL 上下文丢失) -
canvas.style.width和canvas.style.height必须设回原始 CSS 值,比如"400px",否则布局会撑开 - 紧接着调用
ctx.scale(dpr, dpr)—— 这一步让后续所有fillRect(0,0,100,100)仍按逻辑像素写,自动映射到正确物理区域
适配图像与文字资源
画布清晰了,贴进去的图和字还可能糊:
- 图片加载优先用
@2x或@3x版本:img.src = url.replace(/\.png$/,@${Math.round(dpr)}x.png) -
drawImage必须传目标尺寸参数:ctx.drawImage(img, 0, 0, img.width, img.height, x, y, width / dpr, height / dpr) - 字体字号乘以 dpr:
ctx.font =${14 * dpr}px Arial;若已启用ctx.scale(dpr, dpr)`,这样写才匹配视觉大小
监听 dpr 动态变化(推荐)
用户切换显示器、调整系统缩放、进入 PWA 全屏时,dpr 可能突变:
- 用
matchMedia('(resolution)')监听更可靠,比resize事件准确 - 变化后重设 canvas 尺寸、重置 ctx(建议
ctx.setTransform(1, 0, 0, 1, 0, 0)清空缩放状态)、重新绘制内容
不复杂但容易忽略。











