canvas 渲染模糊需重设 canvas.width/height 并配合 ctx.scale(dpr, dpr),contenteditable 发虚受限于浏览器渲染,图标需 js 动态适配 dpr,rem 基准必须取整,且须监听 matchmedia('(resolution)') 捕获 dpr 变化。

Canvas 渲染模糊?必须重设 width/height 属性并配合 ctx.scale
Canvas 在高分屏上发虚,90% 是因为只改了 style.width 和 style.height,没碰 canvas.width 和 canvas.height。这两个 DOM 属性决定绘图缓冲区物理像素数量,CSS 只控制显示尺寸——不匹配就等于拉伸一张低分辨率图。
- 初始化时取
const dpr = window.devicePixelRatio || 1,然后设canvas.width = Math.floor(cssWidth * dpr)、canvas.height = Math.floor(cssHeight * dpr) - 紧接着用 CSS 把显示尺寸缩回:
canvas.style.width = cssWidth + 'px'、canvas.style.height = cssHeight + 'px' - 每次
ctx绘图前调用ctx.scale(dpr, dpr),否则所有坐标都要手动乘dpr,极易漏算或重复缩放 - 别在
scale后再传物理像素值给fillRect(x, y, 1, 1)——它该写成fillRect(x, y, 1, 1),逻辑坐标不变
contenteditable 光标/文字发虚?绕不开浏览器亚像素渲染限制
原生 contenteditable 在高分屏下光标抖动、文字边缘毛糙,不是 JS 能直接修复的,因为文本渲染路径由浏览器底层控制。能做的只有约束输入条件和渲染上下文。
- 给编辑容器加
style="image-rendering: pixelated; image-rendering: -webkit-optimize-contrast;",对 Chrome/Safari 有轻微改善 - 避免用
transform: scale(0.5)模拟缩放——它放大所有插值误差;应改用font-size和line-height按整数dpr等比缩放 -
getSelection().getRangeAt(0).getBoundingClientRect()返回的是 CSS 像素,若用于 Canvas 定位,必须手动乘dpr才能对齐物理像素 - 自定义 SVG 光标时,
stroke-width写死为"1"不够,得配合style="transform: scale(${dpr})"或直接设stroke-width="${dpr}"
图标与背景图不响应 DPR?不能依赖 srcset 或 background-image 自动适配
srcset 只对 <img> 生效,工具栏图标、状态指示器、网格线等常走 background-image 或内联 SVG,它们不支持 srcset,硬编码 @2x 后缀在 Windows 125% 缩放(dpr = 1.25)或 iPad Pro(dpr = 2.8)下会失效。
- 用 JS 动态加载图标资源:根据
window.devicePixelRatio计算最接近的倍率,例如Math.round(dpr)或Math.min(3, Math.ceil(dpr)) - SVG 图标优先用
<svg></svg>内联,关键<path></path>的stroke-width和font-size需按dpr动态设置 - 背景图改用
media查询 + JS 注入 class 控制:document.body.classList.add(`dpr-${Math.round(dpr)}`),再写.dpr-2 .toolbar-bg { background-image: url(icon@2x.png); } - 监听
window.matchMedia('(resolution)')变更,而非仅靠resize——窗口拖到外接显示器时resize不触发,但dpr已变
rem 基准非整数?导致所有 border/padding 渲染为亚像素
很多人以为 rem 能自动适配 DPR,其实它只解决字体缩放,不解决像素对齐。如果 document.documentElement.style.fontSize 算出来是 52.4px,那 border: 1px 最终可能渲染为 1.2 个物理像素,边缘必然模糊。
- 计算基准字号时必须取整:
const base = Math.round(document.documentElement.clientWidth / 7.5),再设document.documentElement.style.fontSize = base + 'px' - 确保
<meta name="viewport" content="width=device-width, initial-scale=1.0">中initial-scale没被 JS 动态修改,否则clientWidth读数失真 - 避免用
margin-left: 0.5px微调——不同dpr下渲染位置漂移,改用transform: translateX(1px)并加will-change: transform -
viewport缩放变化时,dpr可能同步变(Chrome/Firefox 支持,Safari 部分版本不响应),需监听matchMedia回调重算
实际中容易被忽略的是:DPR 变化不总是伴随 resize,比如用户从笔记本切换到外接 Retina 显示器,或者 Windows 切换缩放比例,此时仅靠窗口事件无法捕获。必须把 matchMedia('(resolution)') 和 window.devicePixelRatio 一起纳入初始化和重置流程,否则部分设备上永远卡在旧值。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











