移动端如何画 0.5px 细线?3 种方案原理与实现
移动端高清屏上 1px 线太粗,本质是设备像素比(DPR)的锅。CSS 的 1px 在 2 倍屏上渲染成 2 个物理像素,在 3 倍屏上渲染成 3 个。想要真正 0.5px 的细线,业界主流有三种方案。
transform + 伪元素是最稳的:用 ::after 画 1px 边框,再 scaleY(0.5) 缩一半。伪元素独立缩放,不影响容器内子元素。需要适配 3 倍屏时,DPR 为 3 的设备用 scaleY(0.333)。
css.hairline::after { content: ''; position: absolute; left: 0; bottom: 0; width: 100%; border-bottom: 1px solid #ccc; transform: scaleY(0.5); transform-origin: 0 0; } /* 3 倍屏适配 */ @media (-webkit-min-device-pixel-ratio: 3) { .hairline::after { transform: scaleY(0.333); } }
Vant、Ant Design Mobile 等组件库底层就是这个方案。
meta viewport 缩放:把页面整体 initial-scale=0.5,然后正常写 1px。但这会让字号、间距全缩小一半,还得手动把所有尺寸乘 2 补回来,工程成本太高,几乎没人用。
SVG / Canvas:stroke-width="0.5" 或 lineWidth = 0.5 精确控制像素。只适合画图场景,做边框属于杀鸡用牛刀。
追问
为什么不直接写 border: 0.5px?
Chrome 和大部分 Android 浏览器会把小于 1px 的值当 0 处理或向上取整到 1px。iOS Safari 8+ 虽然支持 0.5px,但 Android 阵营几乎全军覆没,兼容性不可靠。
3 倍屏怎么处理?
3 倍屏(如部分安卓旗舰)CSS 1px 渲染成 3 物理像素,scaleY(0.5) 只缩到 1.5 物理像素,还不够细。正确做法是 scaleY(1/3),通过 @media (-webkit-min-device-pixel-ratio: 3) 匹配后单独处理。
transform 缩放会不会影响点击事件?
不会。transform 只影响视觉渲染层(composite),元素的布局尺寸和事件响应区域不变。伪元素本身也不参与事件传递。
项目里怎么统一处理细线?
封装一个 PostCSS 插件或 Less/Mixin,构建时自动把 1px 边框替换成伪元素方案。Vant 的 border-hairline 类就是这种思路:开发者写 class="van-hairline--bottom",框架自动生成 ::after + scale 代码。
线性渐变和 box-shadow 方案呢?
linear-gradient 画 50% 颜色 + 50% 透明的 1px 条带也能模拟 0.5px,但圆角边框没法用。box-shadow: 0 1px 1px -1px rgba(0,0,0,0.5) 利用负扩展让阴影只露一半,不过颜色控制不精确,深色线效果差。这两种都是备选,生产环境首选 transform 方案。