作者:empty@毛豆前端
前言
移动端web项目越来越多,设计师对于UI的要求也越来越高,比如1px 的边框。在高清屏
下,移动端的1px 会很粗。 比如,这个是假的1像素
这个是真的1像素
一、产生原因
那么为什么会产生这个问题呢?主要是跟一个东西有关,DPR(devicePixelRatio) 设备像素比,它是默认缩放为100%的情况下,设备像素和CSS像素的比值。
window.devicePixelRatio=物理像素 /CSS像素
目前主流的屏幕DPR=2 (iPhone 8),或者3 (iPhone 8 Plus)。拿2倍屏来说,设备的物理像素要实现1像素,而DPR=2,所以css 像素只能是 0.5。一般设计稿是按照750来设计的,它上面的1px是以750来参照的,而我们写css样式是以设备375为参照的,所以我们应该写的0.5px就好了啊! 试过了就知道,iOS 8+系统支持,安卓系统不支持。
二、解决方案
1、WWDC对iOS统给出的方案
推荐指数:**
在 WWDC大会上,给出来了1px方案,当写 0.5px的时候,就会显示一个物理像素宽度的 border,而不是一个css像素的 border。 所以在iOS下,你可以这样写。
border:0.5px solid #E5E5E5
可能你会问为什么在3倍屏下,不是0.3333px 这样的?经过我测试,在Chrome上模拟iPhone 8Plus,发现小于0.46px的时候是显示不出来。 总结:
- 优点:简单,没有副作用
-
缺点:支持iOS 8+,不支持安卓。后期安卓follow就好了。
2、使用边框图片
推荐指数:**
border: 1px solid transparent; border-image: url('./../../image/96.jpg') 2 repeat;
图片自己随便截图的,建议自己做一张图片
图片的颜色就是此后border的颜色
这个方法在W3CPlus 上的例子讲的非常细致 https://www.w3cplus.com/content/css3-border-image 。 总结:
- 优点:没有副作用
-
缺点:border颜色变了就得重新制作图片;圆角会比较模糊。
3、使用box-shadow实现
推荐指数:***
先复习一下box-shadow,看一下MDN 上的这篇就够了https://developer.mozilla.org/zh-CN/docs/Web/CSS/box-shadow 。 再看一下效果
代码是怎样实现的呢?
box-shadow: 0 -1px 1px -1px #e5e5e5, //上边线
1px 0 1px -1px #e5e5e5, //右边线
0 1px 1px -1px #e5e5e5, //下边线
-1px 0 1px -1px #e5e5e5; //左边线
前面两个值 x,y 主要控制显示哪条边,后面两值控制的是阴影半径、扩展半径。 其实方法可以到这个地址线上尝试一下 。
总结
- 优点:使用简单,圆角也可以实现
-
缺点:模拟的实现方法,仔细看谁看不出来这是阴影不是边框。
4、使用伪元素
推荐指数:****
这个方法是我使用最多的,做出来的效果也是非常棒的,直接上代码。
1 条border
.setOnePx{ position: relative; &::after{ position: absolute; content: ''; background-color: #e5e5e5; display: block; width: 100%; height: 1px; /*no*/ transform: scale(1, 0.5); top: 0; left: 0; } }
可以看到,将伪元素设置绝对定位,并且和父元素的左上角对齐,将width 设置100%,height设置为1px,然后进行在Y方向缩小
0.5倍
。4 条border
.setBorderAll{ position: relative; &:after{ content:" "; position:absolute; top: 0; left: 0; width: 200%; height: 200%; transform: scale(0.5); transform-origin: left top; box-sizing: border-box; border: 1px solid #E5E5E5; border-radius: 4px; } }
同样为伪元素设置绝对定位,并且和父元素左上角对其。将伪元素的长和宽先放大2倍,然后再设置一个边框,以左上角为中心,缩放到原来的
0.5倍
总结: - 优点:全机型兼容,实现了真正的1px,而且可以圆角。
-
缺点:暂用了after 伪元素,可能影响清除浮动。
5、设置viewport的scale值
推荐指数:*****
这个解决方案是利用viewport+rem+js 实现的。 参考了网上的一个例子 移动端1像素边框问题的解决方案,自己手动实现了一下。 效果不错。 上代码
<html> <head> <title>1px question</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <meta name="viewport" id="WebViewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"> <style> html { font-size: 1px; } * { padding: 0; margin: 0; } .top_b { border-bottom: 1px solid #E5E5E5; } .a,.b { box-sizing: border-box; margin-top: 1rem; padding: 1rem; font-size: 1.4rem; } .a { width: 100%; } .b { background: #f5f5f5; width: 100%; } </style> <script> var viewport = document.querySelector("meta[name=viewport]"); //下面是根据设备像素设置viewport if (window.devicePixelRatio == 1) { viewport.setAttribute('content', 'width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no'); } if (window.devicePixelRatio == 2) { viewport.setAttribute('content', 'width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no'); } if (window.devicePixelRatio == 3) { viewport.setAttribute('content', 'width=device-width,initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no'); } var docEl = document.documentElement; var fontsize = 32* (docEl.clientWidth / 750) + 'px'; docEl.style.fontSize = fontsize; </script> </head> <body> <div class="top_b a">下面的底边宽度是虚拟1像素的</div> <div class="b">上面的边框宽度是虚拟1像素的</div> </body> </html>
总结
- 优点:全机型兼容,直接写
1px
不能再方便 -
缺点:适用于新的项目,老项目可能改动大
三、踩过的坑
一部血泪史。。。。征服不了UI,只能征服自己。
1、使用
伪元素
方法,伪类里面再设置伪元素,可以选择到吗?看图,需要改中间的竖线 上代码
&:nth-child(2){ //border-color: #e5e5e5 !important; border: 0; position: relative; &:after{ position: absolute; content: ''; background-color: #e5e5e5; display: block; width: 100%; height: 1px; /*no*/ transform: scale(1, 0.5); top: 0; } }
然而上面代码展示出来的样式是这样的 为什么中间的竖线没有了?!最初我以为在伪类下面,再写伪元素after,可能会拿不到。 看到这里发现,是有after伪元素的,但是好像位置不对,跑到上面去了。我是想要竖线的,到底什么原因呢?最后在安哥的指导找了一种方法解决了这个问题,才明白其中的真相。
原来是我的width 和 height 设置的有问题,对于竖线,应该是width =1px,height=100%,然后再缩放 X 方向
0.5倍
,这样竖线就出来了;同样,设置水平线,应该是width=100%,height=1px,然后再缩放Y方向0.5倍
。
知道了原因,再也不担心写错了。
&:nth-child(2){
position: relative;
&:after{
position: absolute;
content: '';
top: 0;
left: 0;
width: 1px;
height: 100%;
transform: scaleX(0.5);
background: #e5e5e5;
transform-origin: 0 0;
}
}
这样是可以的,
&:nth-child(2){
position: relative;
&:after{
content:" ";
position:absolute;
top: 0;
left: 0;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
box-sizing: border-box;
border-left: 1px solid #E5E5E5;
}
}
这样也是可以的。
2、为什么还是拿不到伪元素
?
我以为知道了上面的方法就可以快快乐乐的写1像素 border 了,然而马上又怀疑自己了。
上面这个输入框的1像素,需要的所有的border都是1px,我使用了宽高放大200%后再缩小0.5倍
的方法。
这就奇怪了,同样的方法在别的地方都有效的,为什么在这里不显示了。找了好久,最后找到这篇文章使用 CSS 伪元素需要注意的 ,然后发现:
所以不显示的原因找到了,==输入框Textarea不支持伪元素==、 没办法了,伪元素的方法不能用,只能使用其他的方法了。
四、总结
总结一下,新项目最好使用的是设置viewport的scale值
,这个方法兼容性好,后期写起来方便。老项目的话,改起来可能比较多,用的比较多的方法就是伪元素+transform
的方法。 其他的背景图片,阴影的方法毕竟还是不太灵活,而且兼容性不好。