canvas 在高 dpi 屏幕上模糊的根本原因是未对齐物理像素,需同步设置 canvas 像素尺寸、css 显示尺寸和绘图坐标系缩放,并确保绘图坐标取整、resize 后清空画布及处理 backingstorepixelratio 兼容性。

Canvas 在高 DPI 屏幕上模糊,不是 Canvas 本身有问题,而是你没让它“对齐物理像素”。只要 window.devicePixelRatio ≠ 1,且没做适配,就一定会模糊——无论你用的是 Chrome、Safari 还是 Edge。
为什么直接设置 canvas.width 和 canvas.height 不够
很多人以为把 canvas.width 设成 300 * window.devicePixelRatio 就完事了,但漏掉关键两步:CSS 尺寸没同步缩放回去,绘图坐标系也没重校准。结果就是画布变大了,但页面里它被 CSS 压扁拉伸,或者绘图内容整体偏移、变形、发虚。
- 只改
canvas.width/canvas.height,不设canvas.style.width/canvas.style.height→ 页面中 Canvas 被拉伸,图像模糊 - 设了物理尺寸和 CSS 尺寸,但没调
ctx.scale(dpr, dpr)→ 所有绘图坐标被放大 dpr 倍,图形缩小到左上角,或完全画不出 - 用了
ctx.scale(),但后续坐标仍手动乘了 dpr(比如ctx.fillRect(x * dpr, y * dpr, w * dpr, h * dpr))→ 双重缩放,图形错位、线条变粗、文字糊成一团
setupHiDPICanvas 函数必须包含的三要素
一个真正可用的高 DPI Canvas 初始化函数,必须同时处理尺寸、样式、坐标系。缺一不可。
-
canvas.width和canvas.height设为cssWidth * effectiveDPR和cssHeight * effectiveDPR(注意不是简单用window.devicePixelRatio,要除以backingStorePixelRatio得到更准的effectiveDPR) -
canvas.style.width和canvas.style.height必须显式设为"xxxpx",否则浏览器可能按默认行为渲染(比如继承父容器 width:auto) -
ctx.setTransform(effectiveDPR, 0, 0, effectiveDPR, 0, 0)比ctx.scale()更稳妥——它重置整个变换矩阵,避免叠加缩放残留
绘图时坐标必须对齐整数像素
即使 DPI 适配完全正确,ctx.fillText() 或 ctx.strokeRect() 还是发虚?大概率是坐标含小数。Canvas 对亚像素极其敏感:x=10.5 会触发浏览器插值,边缘立刻毛边。
- 绘制 1px 线条时,确保起始坐标是整数,且
ctx.lineWidth = 1(不要设 1.2 或 0.8) - 文字居中时,别只靠
textAlign = "center",还要保证ctx.fillText(text, x, y)中的y是整数(尤其font-size小于 16px 时) - 用
Math.round()或Math.floor()主动取整,比如const x = Math.round(left + width / 2) - 避免在缩放后做浮点运算再传给绘图 API;先算逻辑坐标,再由
setTransform统一映射
容易被忽略的兼容性细节
有些设备(特别是旧版 Safari 或部分安卓 WebView)的 backingStorePixelRatio 返回 undefined 或 1,导致 effectiveDPR 计算失真。还有 canvas resize 后未清空缓存路径、未重置 globalAlpha 等状态,也会让模糊表现不稳定。
- 始终用
const bsr = ctx.backingStorePixelRatio || 1防 null/undefined - 每次 resize 后执行
ctx.clearRect(0, 0, canvas.width, canvas.height),避免旧像素残留干扰新绘制 - 监听
window.matchMedia('(resolution: ...)').addEventListener或resize事件重新适配,因为用户可能动态缩放页面或切换显示器 - 移动端 Safari 有时会忽略
canvas.style.imageRendering = 'crisp-edges',但设置image-rendering: -webkit-optimize-contrast可辅助强化像素对齐
最麻烦的不是写不对代码,而是写对了却忘了某次绘图用了小数坐标,或者 resize 后没清空 canvas —— 这些地方一漏,模糊就回来,还很难复现。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











