因为1px是CSS逻辑像素而非物理像素,iPhone 13等高DPR设备(如dpr=3)下1px会渲染为3物理像素,导致视觉变粗;伪元素+transform方案需父元素relative定位、伪元素设2px再scaleY(0.5)、指定transform-origin并加pointer-events:none。
border: 1px 在手机上总显得粗?因为 1px 是 CSS 逻辑像素,不是物理像素。iPhone 13 的 window.devicePixelRatio 是 3,意味着你写的 1px 实际占用了 3 个物理像素——它本就不是“细线”,只是被浏览器“凑合渲染”出来的视觉残留。这不是 bug,是设备特性。不处理的话,边框在安卓中可能虚、在 iOS 中可能发糊、在高刷屏上还容易闪烁。
::after + transform: scaleY(0.5) 怎么写才真正生效?关键不在缩放本身,而在定位基点和尺寸控制。很多代码抄来就失效,是因为漏了这几点:
position: relative 必须加在父元素上,否则伪元素会相对于最近的定位祖先偏移height: 1px 写在伪元素里是“CSS 像素高度”,不是目标物理像素;缩放前得先让它有足够物理像素可缩(比如设 height: 2px 再 scaleY(0.5))transform-origin 要匹配方向:底部边框用 bottom,右侧边框用 right,否则缩放后会错位或裁切pointer-events: none,否则伪元素会拦截点击事件(尤其表单控件里很致命)正确示例(底部边框):
.cell { position: relative;}.cell::after { content: ''; position: absolute; left: 0; bottom: 0; width: 100%; height: 2px; background-color: #e0e0e0; transform: scaleY(0.5); transform-origin: bottom; pointer-events: none;}
devicePixelRatio 写多档媒体查询?绝大多数场景下——不用。主流设备 DPR 集中在 2 和 3,scaleY(0.5) 对 DPR=2 完美映射为 1 物理像素;DPR=3 时虽略超(缩放后是 1.33px),但人眼几乎不可辨,且比硬切 scaleY(0.333) 更稳定。真要兼容 DPR=3+ 设备(如部分折叠屏、iPad Pro),只建议在关键 UI 区域(如 Tab 栏分隔线)加一层判断:
立即学习“前端免费学习笔记(深入)”;
@media (-webkit-min-device-pixel-ratio: 3), (min-resolution: 3dppx) { .cell::after { transform: scaleY(0.333); }}
但注意:min-resolution: 3dppx 在旧版 Android WebView 中不支持,如果项目还要兼容 Android 4.x,宁可统一用 0.5,别引入 fallback 失败风险。
不是所有元素都适合套伪元素边框:
<input>、<textarea> 等替换元素无法添加 ::before/::after,得换 box-shadow 或包裹一层容器::after 做其他用途(如清除浮动、图标插入)的组件,再叠加边框会导致层叠混乱,必须改用 ::before 或额外 DOM 节点border-radius 到伪元素,且值要翻倍(因为缩放后半径也缩了),例如主体 border-radius: 4px,伪元素就得写 border-radius: 8px
真正难的不是写出 1px,而是让这个 1px 在各种边界条件下不破、不闪、不挡事——缩放只是手段,控制上下文才是关键。