移动端适配概念及1px问题

448 阅读5分钟

关于分辨率

通常我们所说的分辨率就两种:屏幕分辨率、图片分辨率。

屏幕分辨率,就是指一块屏幕由多少个像素方块组成。比如像我的 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个物理像素来显示。

本来它们应该都差不多大小的,见下图:

alt微信的图

但是,因为像素点的大小不确定,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 为多少,也能保证视觉上的基本统一,防止过细的现象

目前来讲,视觉效果上我认为有赞这种处理是最好的。