移动端1px问题

起因

UI设计稿

移动端H5开发中,UI设计稿中边框为1px,前端在开发过程中如果出现border:1px(或width: 1px,height: 1px,…),测试会发现在HiDPI或Retina屏机型中,1px会比较粗。

设备像素比

设备像素比:dpr=window.devicePixelRatio,也就是设备的物理像素与逻辑像素的比值,它告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素。

在HiDPI或Retina屏的手机上, dpr常见的有23css里写的1px宽度映射到物理像素上就有2px3px宽度。

例如:iPhone6dpr2,物理像素是750x轴),它的逻辑像素为375。也就是说,1个逻辑像素,在x轴和y轴方向,需要2个物理像素来显示,即:dpr=2时,表示1个CSS像素由4个物理像素点组成。

img-01

解决

伪类+transform

移动端京东处理1px有用到伪类+transform

tips

伪类+transform可以很好的解决部分手机不支持.5px显示问题,关键是利用好scale()

优点:所有场景都能满足,支持圆角(伪类和本体类都需要加border-radius)。
缺点:代码量也很大,对于已经使用伪类的元素(例如clearfix),可能需要多层嵌套。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/* 手机端实现真正的1px边框 */
.border-1px,
.border-bottom-1px,
.border-top-1px,
.border-left-1px,
.border-right-1px {
position: relative;
}

/* 线条颜色 */
.border-1px::after,
.border-bottom-1px::after,
.border-top-1px::after,
.border-left-1px::after,
.border-right-1px::after {
background-color: #e9e9e9;
}

/* 底边边框1px */
.border-bottom-1px::after {
content: "";
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}

/*上边边框1px*/
.border-top-1px::after {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 1px;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}

/* 左边边框1px */
.border-left-1px::after {
content: "";
position: absolute;
left: 0;
top: 0;
width: 1px;
height: 100%;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}

/* 右边边框1px */
.border-right-1px::after {
content: "";
position: absolute;
right: 0;
top: 0;
width: 1px;
height: 100%;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}

/* 边框1px */
.border-1px::after {
content: "";
-webit-box-sizing: border-box;
box-sizing: border-box;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
border: 1px solid #e9e9e9;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}

/* 设备像素比 */
/* 显示屏最小dpr为2 */
@media (-webkit-min-device-pixel-ratio: 2) {
.border-bottom-1px::after,
.border-top-1px::after {
-webkit-transform: scaleY(.5);
transform: scaleY(.5);
}

.border-left-1px::after,
.border-right-1px::after {
-webkit-transform: scaleX(.5);
transform: scaleX(.5);
}

.border-1px::after {
width: 200%;
height: 200%;
-webkit-transform: scale(.5);
transform: scale(.5);
}
}

/* 显示屏最小dpr为3 */
@media (-webkit-min-device-pixel-ratio: 3) {
.border-bottom-1px::after,
.border-top-1px::after {
-webkit-transform: scaleY(.333);
transform: scaleY(.333);
}

.border-left-1px::after,
.border-right-1px::after {
-webkit-transform: scaleX(.333);
transform: scaleX(.333);
}

.border-1px::after {
width: 200%;
height: 200%;
-webkit-transform: scale(.333);
transform: scale(.333);
}
}

viewport + rem

优点:所有场景都能满足,一套代码,可以兼容基本所有布局。
缺点:老项目修改代价过大,只适用于新项目。

1
2
3
4
5
6
7
8
9
!function() {
var dpr = window.devicePixelRatio || 1
var scale = 1 / dpr
var viewport = document.querySelector("meta[name=viewport]")
viewport.setAttribute = ('content', 'width=device-width,user-scalable=no,initial-scale=' + scale)
var docEl = document.documentElement
var fontsize = docEl.clientWidth / 10 * dpr + 'px'
docEl.style.fontSize = fontsize
}()

background-image

background-imageborder-image的方法一样,你要先准备一张符合你要求的图片。

优点:可以设置单条,多条边框,没有性能瓶颈的问题。
缺点:修改颜色麻烦, 需要替换图片;圆角需要特殊处理,并且边缘会模糊;兼容性差。

1
2
3
4
.background-image-1px {
background: url(../img/line.png) repeat-x left bottom;
background-size: 100% 1px;
}

postcss-write-svg

在实际开发中,postcss-write-svg 用的很少,仅适合直线,一般不考虑。

借助于PostCSS的插件postcss-write-svg,无需background-imageborder-image的方法那样依赖图片。

1
2
3
4
5
6
7
8
9
10
11
12
@svg border1px {
height: 2px;
@rect {
fill: var(--color, black);
width: 100%;
height: 50%;
}
}
.example {
border: 1px solid transparent;
border-image: svg(border1px param(--color #00b1ff)) 2 2 stretch;
}

这样PostCSS会自动帮你把CSS编译出来:

1
2
3
4
5
.example {
border: 1px solid transparent;
border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='2px'%3E%3Crect fill='%2300b1ff' width='100%25' height='50%25'/%3E%3C/svg%3E")
2 2 stretch;
}

总结

通常情况,伪类元素方案更好,无论是从成本还是灵活性出发。

相关链接

[1] MDN - Window.devicePixelRatio

[2] 移动端 1px 解决方案(完整版)

[3] MDN - 标准元数据名称

[4] 前端提高篇(四十六)CSS进阶11:移动端布局(等比缩放+1px问题:viewport+rem | transform | vw+插件)

[5] postcss-write-svg