移动端 1px 像素问题解决方案
解决 Retina 屏幕下 CSS 1px 边框过粗的问题
问题
在 Retina 屏幕(DPR >= 2)上,CSS 的 1px 会被渲染成 2 个或更多物理像素,导致边框看起来比设计稿粗。如何让 1px 在高清屏上显示为真正的 1 物理像素?
解答
方案一:伪元素 + transform(推荐)
最常用的方案,兼容性好:
/* 单边框 */
.border-bottom {
position: relative;
}
.border-bottom::after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background: #000;
transform: scaleY(0.5);
transform-origin: 0 0;
}
/* 四边框 */
.border-all {
position: relative;
}
.border-all::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 200%;
height: 200%;
border: 1px solid #000;
transform: scale(0.5);
transform-origin: 0 0;
box-sizing: border-box;
pointer-events: none; /* 避免遮挡点击 */
border-radius: 8px; /* 如需圆角,设为实际值的 2 倍 */
}
方案二:根据 DPR 动态设置
使用 JavaScript 检测 DPR,动态调整缩放比例:
// 获取设备像素比
const dpr = window.devicePixelRatio || 1;
// 设置 CSS 变量
document.documentElement.style.setProperty('--dpr', dpr);
document.documentElement.style.setProperty('--border-scale', 1 / dpr);
.border-bottom::after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background: #000;
transform: scaleY(var(--border-scale, 0.5));
}
方案三:使用 box-shadow
简单但颜色会变浅:
.border-bottom {
box-shadow: 0 1px 0 0 #000;
}
/* 0.5px 的阴影在 DPR=2 的屏幕上接近 1 物理像素 */
.border-bottom-thin {
box-shadow: 0 0.5px 0 0 #000;
}
方案四:使用 SVG border-image
精确控制,但写法复杂:
.border-bottom {
border-bottom: 1px solid transparent;
border-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1' height='1'%3E%3Crect width='1' height='0.5' fill='%23000'/%3E%3C/svg%3E") 0 0 1 0 stretch;
}
方案五:viewport 缩放
整体缩放页面,需要配合 rem 使用:
<script>
const dpr = window.devicePixelRatio || 1;
const scale = 1 / dpr;
// 设置 viewport
document.querySelector('meta[name="viewport"]').setAttribute(
'content',
`width=device-width,initial-scale=${scale},maximum-scale=${scale},minimum-scale=${scale},user-scalable=no`
);
// 设置根字体大小,配合 rem 使用
document.documentElement.style.fontSize = dpr * 100 + 'px';
</script>
/* 使用 rem,1rem = 100px * dpr */
.box {
width: 1rem; /* 实际 100px */
border: 1px solid #000; /* 真正的 1 物理像素 */
}
封装通用 Sass Mixin
// 1px 边框 mixin
@mixin border-1px($color: #000, $direction: bottom, $radius: 0) {
position: relative;
&::after {
content: '';
position: absolute;
pointer-events: none;
@if $direction == bottom {
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background: $color;
transform: scaleY(0.5);
} @else if $direction == top {
left: 0;
top: 0;
width: 100%;
height: 1px;
background: $color;
transform: scaleY(0.5);
} @else if $direction == all {
top: 0;
left: 0;
width: 200%;
height: 200%;
border: 1px solid $color;
border-radius: $radius * 2;
transform: scale(0.5);
transform-origin: 0 0;
box-sizing: border-box;
}
}
}
// 使用
.list-item {
@include border-1px(#e5e5e5, bottom);
}
.card {
@include border-1px(#ddd, all, 8px);
}
关键点
- 问题根源:DPR > 1 时,1 个 CSS 像素对应多个物理像素,导致边框变粗
- 主流方案:伪元素 + hd18w scale 缩放,兼容性好,使用灵活
- 缩放比例:DPR=2 时缩放 0.5,DPR=3 时缩放 0.33
- 圆角处理:四边框方案中,border-radius 需设为实际值的 2 倍
- pointer-events:伪元素要设置
pointer-events: none避免遮挡交互
目录