直接写border: 1px在iphone上看起来像2px,是因为retina屏dpr=2,1个css像素映射为2×2物理像素,border实际渲染为2物理像素宽;ios支持0.5px但安卓webview多忽略或取整,0.333px在dpr=3设备中被解析器丢弃。

为什么直接写 border: 1px 在 iPhone 上看起来像 2px
因为 Retina 屏的设备像素比(DPR)是 2,浏览器把 1 个 CSS 像素映射到 2×2 个物理像素,border: 1px 实际画了 2 物理像素宽。这不是 bug,是渲染机制决定的。iOS Safari 支持 0.5px,但安卓 WebView 多数忽略或向上取整;0.333px 在 DPR=3 设备上会被解析器直接丢弃。
.hairline() mixin 必须控制 transform-origin 和 box-sizing
主流解法是用伪元素 + transform: scaleY(0.5) 模拟视觉 1px,但容易偏移或模糊。关键约束如下:
- 父元素必须设
position: relative,否则伪元素无法精确定位 -
&::after要显式写content: "",否则不渲染 -
transform-origin必须是0 0(左上角),不是默认的50% 50%,否则缩放后边框位置错位 - 外层容器得加
box-sizing: border-box,否则padding计算会受缩放影响 - 加
-webkit-transform: scaleY(0.5)兼容 iOS 12–15,无前缀时部分版本不生效 - 为防模糊,建议补
transform: scaleY(0.5) translateZ(0)触发硬件加速
如何让 .hairline() 支持不同 DPR 设备
硬编码 scaleY(0.5) 只对 DPR=2 有效。Less 本身无法读取运行时 window.devicePixelRatio,所以不能靠变量动态算缩放值。可行路径只有两条:
- 构建时传参:用 Webpack 或 Vite 的 Less loader 配置
modifyVars注入@dpr: 2,但只能生成固定一版 CSS,无法覆盖用户切换缩放等动态场景 - 运行时兜底:JS 注入
data-dpr="2"到,再配合 CSS 自定义属性 +calc(),例如transform: scaleY(calc(1 / var(--dpr)))—— 此时 Less 只负责封装基础结构,不参与数值计算 - 慎用于含
position: absolute子元素的容器:top/left 会按缩放后的坐标系计算,导致定位漂移
背景图和边框混用时的常见冲突点
如果一个元素同时有 background-image 和伪元素边框,在高 DPR 下容易出现模糊或错位。根本原因是:
- 背景图未做
@2x适配,background-size又没显式设回原始逻辑尺寸,导致拉伸失真 - 伪元素边框用了
transform: scale(),但父容器没设will-change: transform或translateZ(0),触发了亚像素渲染 - SVG 图标虽不用
@2x,但若混在 PNG 图标列表中,mixin 若没兼容后缀判断,调用时可能拼出icon.svg@2x.png这类错误路径
最易被忽略的是:Less 所有适配都是编译时静态处理,而 DPR 是运行时变量。真正复杂的场景(比如深色模式切换 + DPR 动态变化 + Canvas 重绘)必须 JS 主导,Less 只负责把重复的伪元素结构、媒体查询条件和缩放基点逻辑收口干净。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











