解决手机端border1px变粗的问题

为何会变粗

如iphone 5s,2倍高清屏,css上使用的border:1px对应的是设备独立像素,而此时,这1px会被转成4个物理像素(放大2倍,如下图)。所以,在手机上看到的边框(横向)其实是占用了2个物理像素的高度(如果有对比的话,会非常明显感觉边框高了一倍)。同理在3倍高清屏上,也是一样。

解决的方法都是差不多,使用其他元素来替代border,然后根据不同的屏幕来缩放大小,2倍就50%,3倍就33.333%。

css

物理像素(physical pixel)

物理像素又被称为设备像素,他是显示设备中一个最微小的物理部件。每个像素可以根据操作系统设置自己的颜色和亮度。正是这些设备像素的微小距离欺骗了我们肉眼看到的图像效果。

设备独立像素(density-independent pixel)

设备独立像素也称为密度无关像素,可以认为是计算机坐标系统中的一个点,这个点代表一个可以由程序使用的虚拟像素(比如说CSS像素),然后由相关系统转换为物理像素。

CSS像素

CSS像素是一个抽像的单位,主要使用在浏览器上,用来精确度量Web页面上的内容。一般情况之下,CSS像素称为与设备无关的像素(device-independent pixel),简称DIPs。

设备像素比(device pixel ratio)

设备像素比简称为dpr,其定义了物理像素和设备独立像素的对应关系。它的值可以按下面的公式计算得到:

设备像素比 = 物理像素 / 设备独立像素

1、使用scale

举个例子,现在要做一个上边框 border-top ,这里是用了伪类元素:before来模拟这条上边框,样式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ~border-top
div.ui-border-top:before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: auto;
right: auto;
height: 1px;
width: 100%;
background-color: #c8c7cc;
display: block;
-webkit-transform-origin: 50% 0%;
transform-origin: 50% 0%;
}

然后就需要根据不同的屏来缩放大小,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 2倍高清屏
@media screen and (-webkit-min-device-pixel-ratio: 2){
div.ui-border-top:before {
-webkit-transform: scaleY(0.5); //因为这里只需要缩放y轴
transform: scaleY(0.5);
}
}
// 3倍高清屏
@media screen and (-webkit-min-device-pixel-ratio: 3){
div.ui-border-top:before {
-webkit-transform: scaleY(0.33);
transform: scaleY(0.33);
}
}

2、使用gradient

这种方法的局限性比较大,因为需要使用到元素的背景属性,大概的思路就是利用渐变,来达到线条变小了的效果(其实只是看起来小了)。

1
2
3
4
5
6
7
8
9
10
@media screen and (-webkit-min-device-pixel-ratio: 2){
div.ui-border-top {
width: 100px;
height: 100px;
margin: 0 auto;
background-size: 100% 1px; //1px高度同样翻倍
background-repeat: repeat-x;
background-position: left top;
background-image: -webkit-linear-gradient(bottom, transparent, transparent, #ddd); //使用线性渐变,Linear Gradient (with Even Stops)
}

在线Demo

如果需要适配3倍高清屏,就需要把渐变再分细一层。

3、制作圆角边框

同样使用伪类元素:before来代替。因为有圆角,所以上面提到的方法就行不通了,那么这里使用的方法就是制作一个2倍大小的元素(对应2倍高清屏),然后缩小一倍,来达到1px边框的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@media screen and (-webkit-min-device-pixel-ratio: 2){
.ui-border-radius:before {
content: "";
width: 200%; //2倍大小
height: 200%;
position: absolute;
top: 0;
left: 0;
border: 1px solid #e0e0e0;
-webkit-transform: scale(0.5); //缩小50%
-webkit-transform-origin: 0 0;
padding: 1px;
-webkit-box-sizing: border-box;
border-radius: 8px;
pointer-events: none;
}
}

4、@supports

@supports并不是规范,兼容性也不乐观。但在iOS9里,可以直接使用0.5px,还是非常方便的。

1
2
3
4
5
6
// 这里随意写了个条件
@supports(display: flex){
.demo{
border: 0.5px solid #ddd;
}
}

5、总结

整个实现的方法都是通过使用别的元素来模拟边框,然后根据不同的屏幕来缩放,建议使用伪类元素来替代,这样就不会影响到html的元素布局。(ps:以上的方法并未进行兼容测试,不过可以默认webkit内核的都可以,部分渣渣安卓手机可能也不太兼容!)

参考资料:frozenui
参考资料:Flexible