前言
对于前端开发来说,如何完美的还原UI设计图也是体现了开发功底的,设计师对于我们的要求也是越来越高。例如,如何实现移动端边框1px?为什么1px在不同的设备下展示的粗细不一样?
产生原因
说到这里,我们不得不介绍一下DPR(devicePixelRatio) 设备像素比,它是默认缩放为100%的屏幕下,设备像素和css像素的比值。
DPR(设备像素比) = 物理像素 / css像素
目前比较主流的设备的DPR=2,也有DPR=3的。当我们的DPR为2,也就是2倍屏时,当物理像素(设备像素)为1px的时候,我们的css像素应该是0.5px。当DPR=3,物理像素为1时,css像素应该为1/3px。对于我们的设计图来说,一般情况下,设计稿是按照750来设计的,而我们写css的样式是参考375的屏,所以我们写的css像素应该为设计稿的像素的一半。设计图为1px,我们css像素应该为0.5px,再考虑到设备的像素比,我们写的css应该是0.25px;
解决方案
1、wwdc对ios系统给出的方案
在 WWDC大会上,对ios8+的并且是DPR=2的设备来说,给出来了1px方案,当写 0.5px的时候,就会显示一个物理像素宽度的 border,而不是一个css像素的 border。 所以在iOS下,你可以这样写
border:0.5px solid #E5E5E5
缺点: 只使用于ios8+,安卓不适用(通常3倍屏设备也适用0.5px来解决)
2、 使用边框图片
这个方法在W3CPlus 上的例子讲的非常细致 https://www.w3cplus.com/content/css3-border-image。
border: 1px solid transparent;
border-image: url('./../../image/96.jpg') 2 repeat;
3、使用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; //左边线
4、使用伪元素
//一条border
.setOnePx{
position: relative;
&::after{
position: absolute;
content: '';
background-color: #e5e5e5;
display: block;
width: 100%;
height: 1px;
transform: scale(1, 0.5);
top: 0;
left: 0;
}
}
//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;
}
}
-
优点:全机型兼容,实现了真正的1px,而且可以圆角。
-
缺点:暂用了after 伪元素,可能影响清除浮动。
5、设置viewport的scale值
这个解决方案是利用viewport+rem+js 实现的。
<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
不能再方便 -
缺点:适用于新的项目,老项目可能改动大
6、通过@midia手写border
.min-device-pixel-ratio(@scale2, @scale3) {
@media screen and (min-device-pixel-ratio: 2), (-webkit-min-device-pixel-ratio: 2) {
transform: @scale2;
}
@media screen and (min-device-pixel-ratio: 3), (-webkit-min-device-pixel-ratio: 3) {
transform: @scale3;
}
}
.border-1px(@color: #DDD, @radius: 2PX, @style: solid) {
&::before {
content: "";
pointer-events: none;
display: block;
position: absolute;
left: 0;
top: 0;
transform-origin: 0 0;
border: 1PX @style @color;
border-radius: @radius;
box-sizing: border-box;
width: 100%;
height: 100%;
@media screen and (min-device-pixel-ratio: 2), (-webkit-min-device-pixel-ratio: 2) {
width: 200%;
height: 200%;
border-radius: @radius * 2;
transform: scale(.5);
}
@media screen and (min-device-pixel-ratio: 3), (-webkit-min-device-pixel-ratio: 3) {
width: 300%;
height: 300%;
border-radius: @radius * 3;
transform: scale(.33);
}
}
}
.border-top-1px(@color: #DDD, @style: solid) {
&::before {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
border-top: 1Px @style @color;
transform-origin: 0 0;
.min-device-pixel-ratio(scaleY(.5), scaleY(.33));
}
}
.border-bottom-1px(@color: #DDD, @style: solid) {
&::after {
content: "";
position: absolute;
left: 0;
bottom: 0;
width: 100%;
border-bottom: 1Px @style @color;
transform-origin: 0 0;
.min-device-pixel-ratio(scaleY(.5), scaleY(.33));
}
}
.border-left-1px(@color: #DDD, @style: solid) {
&::before {
content: "";
position: absolute;
left: 0;
top: 0;
height: 100%;
border-left: 1Px @style @color;
transform-origin: 0 0;
.min-device-pixel-ratio(scaleX(.5), scaleX(.33));
}
}
总结:
一般情况下,我们采用移动端新写的项目都采用viewport的scale方法来实现1px的边框,兼容性好。老项目的话建议还是采用伪元素加transform这种方式。
参考文章:
https://juejin.im/post/5d19b729f265da1bb2774865
https://github.com/dengwb1991/owl-ui/blob/master/src/styles/common/border.less