解决retina模糊最有效方法是用devicepixelratio动态设置canvas物理像素尺寸并配合ctx.scale(dpr,dpr)缩放坐标系,确保逻辑坐标与高分屏物理像素匹配。

直接用两倍画布渲染加 CSS 等比缩放,是解决 Retina 模糊最常用也最有效的做法。关键不是“放大两倍”本身,而是让 canvas 的物理像素数量真正匹配高分屏的显示需求,同时保持逻辑坐标系不变。
明确区分两个 width/height
canvas 元素有两个尺寸概念,必须分开设置:
- DOM 属性 width 和 height:决定绘图缓冲区大小(即实际渲染分辨率),单位是物理像素
- CSS 样式 width 和 height:只控制它在页面上占多大视觉空间,单位是 CSS 像素
比如你希望 canvas 在页面上显示为 300×200px,但在 2x 屏上要清晰,就得让内部缓冲区变成 600×400 物理像素,再用 CSS 把它“压回”300×200px 的视觉尺寸。
按 devicePixelRatio 动态计算尺寸
不能写死 “2 倍”,而要读取当前设备的真实像素比:
- 用 window.devicePixelRatio 获取当前缩放系数(Retina Mac 通常是 2,iPhone 13+ 是 3,Windows 高缩放下可能是 1.25/1.5/2)
- 获取 canvas 的目标显示尺寸(推荐用 getBoundingClientRect() 或父容器宽高,避免 offsetWidth 受 margin/padding 干扰)
- 设置:
canvas.width = 显示宽度 × dpr
canvas.height = 显示高度 × dpr
canvas.style.width = 显示宽度 + 'px'
canvas.style.height = 显示高度 + 'px'
必须调用 ctx.scale(dpr, dpr)
光改 canvas 尺寸还不够。此时绘图上下文的坐标系也跟着变“密”了——原来画在 (100, 100) 的点,现在实际落在第 200 个物理像素上。scale 能把坐标系“拉回来”:
- 执行 ctx.scale(dpr, dpr) 后,所有 draw 操作仍按 CSS 像素写坐标(如 ctx.fillRect(10, 10, 20, 20)),Canvas 自动映射到对应物理像素区域
- 不加这步,图形会缩小成原来的 1/dpr,甚至完全看不见
- 建议在每次 resize 后重置:先 ctx.setTransform(1, 0, 0, 1, 0, 0) 清除旧缩放,再 scale
响应式场景下的注意事项
如果 canvas 容器尺寸会随窗口变化(比如全屏或 flex 布局),需监听 resize 并重新计算:
- 用 addEventListener('resize', ...) 不够可靠,推荐用 ResizeObserver 监听父容器变化
- 避免频繁重绘:resize 时只更新 canvas 尺寸和 ctx.scale,不重复执行复杂绘制逻辑
- 导出图片(toDataURL/toBlob)前,确保使用的是物理像素尺寸——此时导出的图天然就是高清的
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











