要在retina屏上绘制真正清晰的1px物理线条,必须使线条精准落在物理像素中心(css坐标×dpr后为.5)、设置linewidth=1/dpr、关闭抗锯齿(imagesmoothingenabled=false、linecap='butt')。

要在Retina屏上用Canvas绘制真正清晰的1px物理线条(即视觉上“极细”且不模糊),关键不是画1个CSS像素宽的线,而是让这条线精准占据1个物理像素,并关闭抗锯齿干扰。直接写 ctx.lineWidth = 1 在高DPR设备上会渲染成2×2或3×3模糊块,必须从坐标、线宽、渲染设置三方面协同控制。
确保线条落在整数物理像素中心
Canvas默认使用“半像素偏移”规则:当坐标为整数时,线实际画在两个像素交界处,触发抗锯齿;只有把线画在**物理像素中心**(即 CSS 坐标 × DPR 后为 .5 结尾),才能填满单个物理像素。
- 先获取设备像素比:
const dpr = window.devicePixelRatio || 1; - 若目标是“视觉1px宽”的竖线,设其x坐标为
Math.round(x * dpr) + 0.5,再除以dpr还原为逻辑坐标传入绘图函数 - 例如:在CSS位置 x=10 处画一条视觉1px竖线:
ctx.beginPath(); ctx.moveTo(10 + 0.5 / dpr, y1); ctx.lineTo(10 + 0.5 / dpr, y2); ctx.stroke();
手动设置线宽为物理1像素
不要依赖 lineWidth = 1,它始终按当前坐标系单位解释。你已通过 ctx.scale(dpr, dpr) 缩放了上下文,此时 lineWidth = 1 实际对应 dpr 个物理像素——太粗。
- 若未缩放上下文(推荐做法):直接设
ctx.lineWidth = 1 / dpr;,并确保所有坐标也按dpr换算 - 若已调用
ctx.scale(dpr, dpr):则设ctx.lineWidth = 1 / dpr;(因为缩放后单位变小了) - 更稳妥统一写法:
ctx.lineWidth = 1 / dpr;+ 所有坐标保持CSS逻辑值,不额外乘除
关闭抗锯齿与图像平滑
即使坐标和线宽正确,Canvas默认开启的抗锯齿仍会让边缘发灰。对精确线条必须禁用:
-
ctx.imageSmoothingEnabled = false;(影响 drawImage,间接关联重采样) -
ctx.lineCap = 'butt'; ctx.lineJoin = 'miter';避免端点/转角额外扩展 - 可选增强:添加
ctx.miterLimit = 1;防止尖角被截断变钝
验证是否生效的小技巧
最直接的方法是放大截图观察:用鼠标选中Canvas区域 → 右键“检查元素” → 截图保存 → 用图片查看器100%缩放查看边缘。清晰锐利的单像素黑线即成功;若有灰边、双线感或虚化,则需回头检查坐标是否带 .5 偏移、lineWidth是否为 1/dpr、以及是否遗漏 imageSmoothingEnabled = false。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











