物理像素,逻辑像素,1px细线问题
物理像素
含义: 设备屏幕实际拥有的像素点,屏幕的基本单元,是有实体的。
比如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)要多层嵌套。
参考:https://www.cnblogs.com/AhuntSun-blog/p/13581877.html
https://juejin.cn/post/6844903797722972168

浙公网安备 33010602011771号