物理像素
含义: 设备屏幕实际拥有的像素点,屏幕的基本单元,是有实体的。
比如iPhone 6的屏幕在宽度方向有750个物理像素点,高度方向有1334个物理像素点,所以iPhone 6 总共有750*1334
个物理像素点。
逻辑像素
含义: 称为设备独立像素(Device Independent Pixel,DIP),也称为CSS像素CSS里定义像素点,比如1px表示逻辑像素为1
详解: 默认情况下1物理像素 = 1逻辑像素, 在高像素密度的设备上1物理像素 = 多个逻辑像素, 比如: 以iPhone6为例,设计稿给出一个图片的宽高为40*40,在实际开发中要除以2,宽高要写成20*20,因为从iPhone4开始, 苹果公司为其产品mac、iPhone以及iPad的屏幕配置了Retina高清屏,在Retian屏上,即 DPR===2。然后现在比较近的,比如iPhone12 mini的DPR是3。
设备像素比(Device Pixel Ratio,DPR)
含义: 设备的物理像素与逻辑像素的比
公式: 物理像素/逻辑像素
我们可以通过window.devicePixelRatio获取设备的dpr
用户缩放浏览器也会引起css中px的变化
当用户把页面放大一倍,那么css中1px所代表的物理像素也会增加一倍;
把页面缩小一倍,css中1px所代表的物理像素也会减少一倍;
即如果发生缩放或者放大,虽然默认情况下一个CSS像素应该是等于一个物理像素的宽度的,但是浏览器的放大操作让一个CSS像素等于了两个设备像素宽度(这时效果就和前面举例iphone中DPR为2一样,放大使得1个CSS的像素px这时变为了两倍)。 从上面的例子可以看出,CSS像素从来都只是一个相对值。
1px细线问题
在上文我们已经知道,CSS像素为1px宽的直线,对应的物理像素是不同的,在不同DPR下,可能是会有实际2倍或者3倍粗,而设计师想要的1px宽的直线,其实就是1物理像素宽,而不是设置1px后被转换成2-3个物理像素。
对于CSS而言,可以认为是border: 0.5px;这是多倍屏下能显示的最小单位。然而,并不是所有手机浏览器都能识别border: 0.5px,有的系统里,0.5px会被当成为0px处理,那么如何1px细线问题呢?
首先可以说一下怎么画一条1像素细线
直接写 0.5px,即通过缩放解决
采用transform: scale()的方式,该方法用来定义元素的2D 缩放转换:
transform: scale(0.5,0.5);
注意scale(0.5,0.5)也可以写(0.2,0.2)画0.2的细线,实际上这个就是用的缩放,所以可以设置其他非0.5
采用meta viewport的方式
<meta name="viewport" content="width=device-width, initial-scale=0.5, minimum-scale=0.5, maximum-scale=0.5"/>
因此很多解决1px问题的话一个常用办法就是缩放
1.viewport 缩放
viewport + rem 实现
同时通过设置对应viewport
的rem
基准值,这种方式就可以像以前一样轻松愉快的写1px
了。
在devicePixelRatio = 1,2,3
时,输出viewport
:
<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>
这种兼容方案相对比较完美,适合新的项目,老的项目修改成本过大。
优点:
所有场景都能满足
一套代码,可以兼容基本所有布局
缺点:
老项目修改代价过大,只适用于新项目
2.伪元素+transform
构建1
个伪元素, border
为1px
, 再以transform
缩放到50%
。
对于老项目,有没有什么办法能兼容1px
的尴尬问题了,个人认为伪类+transform
是比较完美的方法了。
原理是把原先元素的 border
去掉,然后利用 :before
或者 :after
重做 border
,并将 transform
的 scale
缩小一半,原先的元素相对定位,新做的 border
绝对定位。
单条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倍。
四条boder
样式设置:
.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倍。
最好在使用前也判断一下,结合 JS
代码,判断是否 Retina
屏:
if(window.devicePixelRatio && devicePixelRatio >= 2){
document.querySelector('ul').className = 'scale-1px';
}
优点:可以满足所有场景,且修改灵活。缺点:对于已使用伪类的元素(例如clearfix
)要多层嵌套。
然后,还有许多其他办法,比如:使用box-shadow模拟边框,使用border-image,使用background-image,使用box-shadow模拟边框,这些效果都不太好,略。主要还是使用缩放这类比较有效。
参考:https://www.cnblogs.com/AhuntSun-blog/p/13581877.html
https://juejin.cn/post/6844903797722972168