前端
好多做过移动端的同学都知道著名的1px问题,但是为什么会产生这个问题你真的知道么?看完本篇文章,你心里一定会有一个明确的答案
为什么会有1px问题?
要想弄懂这个问题,我们首先要弄懂2个概念,视口和分辨率?
什么是视口(设备独立像素)?
简单来说视口就是显示页面的那部分区域。
- 浏览器:在浏览器中通常与浏览器窗口相同,但不包括浏览器的 UI,菜单栏
- 移动端:
打开
chrome的开发者工具,我们可以模拟各个手机型号的显示情况,每种型号上面会显示一个尺寸,比如iPhone X显示的尺寸是375x812,实际iPhone X的分辨率会比这高很多,这里显示的就是视口大小。
什么是屏幕分辨率(物理像素)
屏幕分辨率指一个屏幕具体由多少个像素点组成。
下面是apple的官网上对手机分辨率的描述:
iPhone XS Max 和 iPhone SE的分辨率分别为2688 x 1242和1136 x 640。这表示手机分别在垂直和水平上所具有的像素点数。
当然分辨率高不代表屏幕就清晰,屏幕的清晰程度还与尺寸有关。
其实上面两个概念是我们经常接触到的,他们还有另外的名字设备独立像素(视口)和设备像素(屏幕分辨率)
设备独立像素(视口)和设备像素(屏幕分辨率)?
- 设备像素(物理像素)(
device pixel->DP)单位:pt,是绝对像素 - 设备独立像素(css像素)(
device independent pixel->DIP)单位:px,是相对像素(相对于设备像素)
设备像素比(DPR)?
DPR(设备像素比)如何计算?
dpr = dp / dip;
dpr=1; 代表一倍屏 1pt = 1px
dpr=2; 代表两倍屏,两倍屏比一倍屏要清楚 2pt = 1px
接下来我们来回答为什么会产生1px的问题
设计同学给我们设计图的时候,往往是按屏幕分辨率给的,比如iphone6的分辨率为750*1334,但是他的设备独立像素为375*667,那么设计图上的每1px我们都要除以2,如果设计图上的border为1px,那么我们要写成0.5px,可是写成0.5px以后,你发现在安卓低端机上不显示,因为0.5px在安卓低端机上相当于0, 所以你写了一个1px,那设计同学一看,肯定是比较粗的呀,让你拿回去改。这就是这个问题的由来。
我们所说的1px和设计同学口中的1px是一样的么?
不一样,设计同学不知道dp,dip,dpr这些,他是按照屏幕分辨率画的图,那么他口中的1px就是1pt, 并不是我们知道的1px(这是重中之重,凡是解决此类问题,只要记住以下公式 1pt = 1px/dpr)
1px解决方案有哪些?他们的优缺点是什么?
所以解决1px的问题其实就是如何在2dpr的设备上显示0.5px,在3drp的设备上显示0.333px。 我们先来想一下如何实现0.5px吧
0.5px实现
.border-1px { border: 1px solid #333 }
@media screen and (min-device-pixel-ratio: 2) {
.border-1px { border: 0.5px solid #333 }
}
/* dpr=2 和 dpr=3 情况下 border 相差无几,下面代码可以省略*/
@media screen and (min-device-pixel-ratio: 3) {
.border-1px { border: 0.333333px solid #333 }
}
优点: 代码简单,使用css即可
缺点: IOS及Android老设备不支持
viewport + rem 实现
通过设置缩放,让 CSS 像素等于真正的物理像素。
const scale = 1 / window.devicePixelRatio;
const viewport = document.querySelector('meta[name="viewport"]');
if (!viewport) {
viewport = document.createElement('meta');
viewport.setAttribute('name', 'viewport');
window.document.head.appendChild(viewport);
}
viewport.setAttribute('content', 'width=device-width,user-scalable=no,initial-scale=' + scale + ',maximum-scale=' + scale + ',minimum-scale=' + scale);
// 设置根字体大小
var docEl = document.documentElement;
var fontsize = 10 * (docEl.clientWidth / 350) + 'px';
docEl.style.fontSize = fontsize;
// 在CSS中用rem单位就行了
缺点:
- 适合新项目一开始就使用,如果是老项目,修改成本比较大
伪元素 + transform 实现
为什么用伪元素? 因为伪元素 ::after 或 ::before 是独立于当前元素,可以单独对其缩放而不影响元素本身的缩放
基于 media 查询判断不同的设备像素比对线条进行缩放:
.border-1px:before{
content: '';
position: absolute;
top: 0;
height: 1px;
width: 100%;
background-color: #999;
transform-origin: 50% 0%;
}
@media (min-device-pixel-ratio:2){
.border-1px:before{
transform: scaleY(0.5);
}
}
@media (min-device-pixel-ratio:3){
.border-1px:before{
transform: scaleY(0.33);
}
}
注意如果需要满足圆角,需要给伪类也加上 border-radius
优点: 兼容性好
综上,推荐使用:
- 伪元素 + transform 实现
- 新项目可以尝试使用
viewport方案
总结
- 我们在从设计师那边拿设计图的时候一定要问清,他是不是按照屏幕分辨率画的,如果是的话,他的
1px就是1pt - 在做移动端问题的时候,可能你的设备并不是iphone等这些知道视口尺寸的设备,也许是天猫精灵,小度音响等这些IOT设备,这些设配你并不知道他的视口大小,你可能只知道屏幕分辨率,所以这个时候你先通过获取宽高,来知道视口尺寸,方便你知道屏幕分辨率和视口的一个关系,也方便你后面做媒体查询
- 在做混合应用的时候,你获取宽高,一定要看清文档,因为你获取的宽高的单位有可能不是px而是pt,所以一定要弄清,方便测试的时候写对尺寸
最后,你看完了本篇文章你觉得1px问题应该谁背锅?