Canvas 在 Retina 屏模糊的根本原因是逻辑像素与物理像素未对齐;需用 window.devicePixelRatio 获取设备像素比,按比例放大 canvas.width/height 并用 ctx.scale(dpr,dpr) 对齐坐标系,同时适配图像和字体资源。

用 window.devicePixelRatio 处理 Retina 屏幕绘图清晰度,核心是让 Canvas 的逻辑像素(CSS 像素)和物理像素对齐——否则在高 DPI 设备上会模糊。
理解 devicePixelRatio 的作用
window.devicePixelRatio 表示设备物理像素与 CSS 像素的比值。普通屏幕通常是 1,Retina 或高清屏常见为 2 或 3。Canvas 渲染模糊的根本原因:你设了 canvas.width = 400、canvas.style.width = "400px",但在 dpr=2 的设备上,这 400px 实际占用了 800 个物理像素,而 canvas 内部只画了 400×400 的点,被拉伸导致模糊。
Canvas 绘图前必须缩放 canvas 元素本身
不能只靠 CSS 缩放,要同时调整 canvas 的 width 和 height 属性(即渲染缓冲区大小),再用 CSS 控制显示尺寸:
- 获取当前 dpr:
const dpr = window.devicePixelRatio || 1; - 设置 canvas 的 CSS 尺寸(用户看到的大小):
canvas.style.width = "400px"; canvas.style.height = "300px"; - 按 dpr 放大 canvas 的内在分辨率:
canvas.width = 400 * dpr; canvas.height = 300 * dpr; - 用 ctx 缩放绘图坐标系,使代码仍按逻辑像素写:
ctx.scale(dpr, dpr);
注意图像资源和字体的适配
即使 Canvas 本身清晰了,如果用 drawImage 加载普通分辨率图片,或用默认字号渲染文字,依然可能发虚:
- 加载图片时优先使用 2x/3x 版本(如
icon@2x.png),或通过image.src = url + `?dpr=${dpr}`请求服务端适配资源 - 绘制文字时,字号也建议乘以 dpr 后再除以 dpr 缩放(配合 ctx.scale),或直接用
ctx.font = `${14 * dpr}px Arial`并取消 ctx.scale,但需同步调整所有坐标 - 避免用
ctx.drawImage(img, x, y)直接贴图,改用带尺寸参数的版本并传入物理像素尺寸:ctx.drawImage(img, 0, 0, img.width, img.height, x, y, img.width/dpr, img.height/dpr)
监听 dpr 变化并动态重绘(可选但推荐)
用户可能切换显示器、缩放浏览器、进入 PWA 全屏等,导致 dpr 改变。可用 window.matchMedia 监听:
matchMedia('(resolution: 2dppx)').addEventListener('change', handleDPRChange)- 或更通用的方式:
window.addEventListener('resize', () => { /* 检查 dpr 是否变化 */ }),对比上次缓存的 dpr 值 - 变化后重新设置 canvas 尺寸、重置 ctx、重绘内容(注意保存绘图状态或数据)
不复杂但容易忽略:dpr 影响的是“画布容量”和“坐标映射”,不是单纯放大。只要 width/height 与 style.width/style.height 比例匹配 dpr,并用 scale 对齐绘图逻辑,就能在 Retina 屏上保持锐利线条和文字。

















