关于分辨率
通常我们所说的分辨率就两种:屏幕分辨率、图片分辨率。
屏幕分辨率,就是指一块屏幕由多少个像素方块组成。比如像我的 iPhone6sp,分辨率是 1920 * 1080,这表示屏幕的水平方向有 1080 个像素方块,竖直方向有 1920 个像素方块。
图像分辨率也是一样的概念,至于清晰程度,当然是相同尺寸的屏幕/图片,谁像素方块多谁清晰。
设备独立像素 DP
刚刚说的分辨率,也就是像素方块,是在物理世界真实存在的,我们称它为物理像素。
而设备独立像素,它不是真实存在的,它是一种尺寸的描述。从低分辨率手机升级为高分辨率手机的过程中,因为屏幕的尺寸基本上是没什么变化的,要在尺寸基本不变的前提下塞进成倍增加的像素方块,每个像素方块的大小就得变小。
所以我们可以得出结论:在低分辩手机 📱 中,像素方块的 size 比较粗大;而在高分辨手机中,像素方块的 size 往往比较细小。
而设备独立像素,则是一种类似物理尺寸的描述。
设备像素比
设备像素比,也称 dpr,它指的是物理像素和设备独立像素的比值。
dpr = 物理像素方块数 / 设备独立像素
像现在个个手机都是高清屏,dpr 都是 2 起步的,3 的也是一抓一大把。
css 像素
css 像素是我们开发中用到最多的一个单位,也就是 px,我们只需记住:当页面缩放比例为 1 时,一个 css 像素就等于一个设备独立像素,至于这个设备独立像素要用多少像素方块画出来,就取决于设备的 dpr 了。
1个css 像素显示大小 = 1个设备独立像素大小 * 页面缩放系数
这个公式是没问题的,在确定的设备下,设备的设备独立像素和物理像素都不会变,假如缩放系数由100%放大为200%,就意味着:1个css 像素显示大小 = 1个设备独立像素大小 * 2,也就是在放大后,一个css像素显示大小现在横跨了两个设备独立像素。
视口
首先我们明确一下,我们常常说的视口一共只有三种:布局视口、视觉视口、理想视口。
那这三个视口有啥区别呢?我们粗浅地认识一下:
- 布局视口。可以认为是一个原始视口,就被称作width,在 pc 端它和浏览器窗口一样,在移动端它会被赋一个默认值,通常为 980px。总之,它不准。(
document.documentElement.clientWidth
) - 视觉视口。用户能看到的真实区域,它决定用户能看到多少东西。(
window.innerWidth
) - 理想视口。网页在移动端展示的最佳大小,也称device-width。(
screen.width
)
我们通常这句width=device-width
,其含义就是让布局视口的宽度等于理想视口的宽度。
此外,由于initial-scale = 理想视口宽度 / 视觉视口宽度
,所以我们设置initial-scale=1
,就可以让视觉视口等于理想视口的宽度。
综上,width=device-width,initial-scale=1.0
,这句的意义在于让三口统一,即:布局视口 = 视觉视口 = 理想视口
。
1px 问题
说回这 1px 问题,我们知道当缩放系数为 1 的时候,1 个 css 像素(px)对应一个设备独立像素。那么在一倍屏(dpr=1)中,就会用 1 个物理像素来显示;而在二倍屏(dpr=2)中,会用2*2
个物理像素来显示;在三倍屏(dpr=3)中,则是用3*3
个物理像素来显示。
本来它们应该都差不多大小的,见下图:
但是,因为像素点的大小不确定,size 可大可小,加上像素间距问题,往往来说两排像素都会比一排像素视觉上宽一些。
而解决 1px 问题的本质,就是让 1px 的 css 像素在任何屏幕中(缩放系数为 1 的前提下),都以一个物理像素(也就是一个像素方块)去显示。
要实现这种效果,就需要用到媒体查询去查设备的 dpr:
/* 这里的,是“或”的意思,下面这行语句用来检查是否为两倍屏 */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx);
市面上处理这个 1px 问题主流的手段有 3 种:
- 使用 background-image(需要引入外部资源,不支持 border-radius,差评)
- 使用 svg(不需要引入外部资源,但同样不支持 border-radius,差评)
- 使用伪元素(不需要引入外部资源,支持 border-radius,好评)
正统的处理方案示例如下:
.retina-border {
position: relative;
}
.retina-border::before {
content: "";
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
transform-origin: left top;
box-sizing: border-box;
pointer-events: none;
border-width: 1px;
border-style: solid;
border-color: #ff414e;
border-radius: 10px;
}
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) {
.retina-border::before {
width: 200%;
height: 200%;
border-radius: 20px;
transform: scale(0.5);
}
}
@media (-webkit-min-device-pixel-ratio: 3), (min-resolution: 3dppx) {
.retina-border::before {
width: 300%;
height: 300%;
border-radius: 30px;
transform: scale(0.33);
}
不过话说回来,在任何屏幕中都用一行物理像素去显示 1px,在视觉上就一定是等宽的吗?答案仍然是不一定。比如,在 dpr 为 4 的超清 2k 屏中,一行物理像素细小得几乎看不见了。
目前对于 1px 像素的处理方案,业界是不统一的,比如像天猫京东这样的大型电商网站,对于移动端 1px 问题也没有进行任何处理。有赞的 vant 则使用了一种比较折中的方案,移动端统一缩放为 0.5,不管 dpr 为多少,也能保证视觉上的基本统一,防止过细的现象。
目前来讲,视觉效果上我认为有赞这种处理是最好的。