1. 前言
在互联网快速发展的今天,手机的更新迭代速度越来越快,手机的种类和尺寸也越来越多,作为前端小伙伴们的我们需要去适配一款又一款的新机型。对于移动端适配,不同的公司、不同的团队有不同的解决方案。
2.1 viewport 的由来
开始之前,先说说iPhone。从iPhone 1代到iPhone 3GS,分辨率是320x480像素,这种设计在当时看起来很完美。
直到2010年的某一天(中考那年),iPhone 4发布了,分辨率为640x960像素,并且屏幕尺寸没有变大,一个像素变成了4个像素,所以同样大小的手机屏幕,像素更多了,像素密度更大了,单个像素更小了。苹果提出了新理念:视网膜屏。当屏幕像素密度超过300ppi时,人眼就无法区分出单独的像素。因此像素密度达到326ppi的iPhone 4具备非常优秀的显示功能,不会再出现颗粒感。
那么问题就来了,原本iPhone1只能看320像素宽的网页,超出320宽就得左右拖动着看。现在iPhone4可以看640像素宽的网页了,虽然是好事,但是640宽的页面挤在手机屏幕上看,字很小,完全看不清。
这时候苹果就给出了一个解决方案,就是著名的
也就是视口(viewport)的概念,苹果的想法是,你们程序猿们做网页就仍然按照320像素的宽度做,但是要加上这行代码,这时候iPhone 4给你们渲染页面的时候,按照四个像素顶一个像素的原则显示,就没问题了。但是如果你不听我的话,不加这样代码,就还是按照640像素的宽度显示。
这样做之后,屏幕显示字体的时候,老机型的大像素只能显示某种单色,而现在大像素切成了四个小像素,这四个像素可以显示4种不同单色,于是削弱了锯齿感,字体也就更清晰了,苹果的目的就达到了。
2.2 一些概念
- CSS 像素(CSS pixels)
指的是CSS样式代码中使用的逻辑像素。
- 设备像素(device pixels)
显示屏幕的最小物理单位,又称为物理像素,每个像素包含自己的颜色、高宽等,不可再细分。这种像素是真实的物理存在的,不可变的,设备一旦造出来就不会变大小和数量。例如手机屏幕说的分辨率1920x1080就是物理像素。
- 设备独立像素(device-independent pixels (dips))
要知道浏览器并不是根据物理硬件的像素来工作的,而是根据 dips 宽度来工作。
还要知道 dips 是将像素值与实际距离联系起来的,不管屏幕的像素密度是多少,dips 为 1px,那么实际宽度就是 1px,也就是对应 CSS 中的 1px,而不是对应物理像素 1px。
- 设备像素比(DPR)
我们知道在 Chrome 浏览器控制台 console 中输入
可以获取当前屏幕的 DPR。
那么什么是 DPR (Device Pixel Ratio)?window.devicePixelRatio 是设备上物理像素 (physical pixels) 和设备独立像素 (device-independent pixels (dips)) 的比例。公式表示就是:
举个栗子,iPhone 6 显示屏分辨率是 1334x750,这个 1334 就是我们前面说的设备上的物理像素值,而显示的高度只有 667,这个就是 dips 值,最终可以知道这台 iPhone 6 屏幕的 DPR 为 2,DPR 在这里所表达的意思就是:667 dips 在实际显示的时候,被硬件扩展到了 1334 的硬件像素宽度,2 个物理像素对应 1 个 CSS 像素(这个指的水平方向或垂直方向,如果在一个平面内的话 4 个物理像素点对应 1 个 CSS 像素点)。
- 设备宽度(device-width)
设备宽度是手机厂商给具体机型内定的一个值,从初代iPhone到5代,它的值都是320px。6代是375px,6plus是414px。这个值可以理解为标准分辨率或者推荐分辨率,就是说你的页面只要按照这个分辨率制作,厂商保证你得到完美效果。
- 视口(viewport)
(1)布局视口(layout viewport):
当没有设定那行著名代码的时候,厂商除了规定标准分辨率,还要给自己的手机设一个最大分辨率。想象一下,假如没有那行代码、也没有layout viewport,这时候你让手机打开一个5000像素宽的页面,那么手机会把5000宽度的页面全部挤到屏幕上,届时,每一个字将成为一个像素点,完全不可看。为了防止这种现象的出现,厂商规定了layout viewport,它的宽度可以通过document.documentElement.clientWidth来获取,通常都是980px。主要意义是在打开大于980宽度的页面的时候可以横向拖动,而不至于挤成一团。
注意:布局视口的宽高值是在页面没添加viewport 时所获得的值。如果你给页面添加了viewport 并且设置了width = device-width 时,通过上面的代码所获得的值就不是布局视口的默认值了。
(2)视觉视口(visual viewport):
视觉视口就是设备的屏幕区域,也就是用户通过屏幕所看到的页面内容,其宽度继承的布局视口宽度。但它所对应的并不是指屏幕区域里的物理像素,而是CSS 像素。对于iPhone 6 Plus来说,在加了著名代码前提下,值是414px,不加的话,值是980px,如果改一改width=device-width, initial-scale=1.5,这时值是276px。所以它是一个可变的值。
(3)理想视口(ideal viewport):
是当width=device-width, initial-scale=1.0的时候,视觉视口的大小。对于iPhone 6 Plus来说,是固定值414px。理想视口就等于设备宽度。
- 像素密度(PPI)
表示每英寸所拥有的像素数量。
同样大小的字体在不同设备为什么看起来大小不一样,本质上就是问一个 dips 像素在不同设备中分别等价于多少英寸。假设 viewport 缩放比例都为 1(即 ideal viewport),那么一个 dips 像素(此时对应一个 CSS 像素)对应的物理像素就是 DPR 个,那么一个 dips 像素的长度就可以由 DPR * (1/PPI) = DPR/PPI 计算出来。
例子:iphone8 PPI 为 326,DPR 为 2,DPR/PPI 约等于 0.00613497,而 iphone8 plus PPI 为401,DPR 为 3,DPR/PPI 约等于 0.0074813;所以 iphone8 plus 上的 app 图标和字比 iphone8 要大。
3. rem,vh、vw ?
3.1 rem布局
rem作用于非根元素时,表示相对于根元素字体大小;rem作用于根元素字体大小时,表示相对于其出初始字体大小。
|
|
所以rem布局的本质是等比缩放,一般是基于宽度。
假设我们将屏幕宽度平均分成100份,每一份的宽度用x表示,x = 屏幕宽度 / 100,如果将x作为单位,x前面的数值就代表屏幕宽度的百分比
|
|
如果想要页面元素随着屏幕宽度等比变化,我们需要上面的x单位(也就是后面会讲到的vw),通过rem这个桥梁,可以模拟上面的x单位
可以发现,如果子元素设置rem单位的属性,通过更改html元素的字体大小,就可以让子元素实际大小发生变化
|
|
如果让html元素字体的大小,恒等于屏幕宽度的1/100,那1rem和1x就等价了
|
|
可以通过下面的js来设置
|
|
那么如何把设计稿中的获取的像素单位的值,转换为以rem为单位的值呢?
公式:元素宽度 / 设计稿宽度 * 100
举个例子,假设设计稿尺寸是640px,设计稿中的一个元素宽度是100px,根据公式100/640*100 = 15.625rem。
下面来验证下上面的计算是否正确,下面的表格是设计稿等比缩放下,元素的宽度
设计稿宽度 | 元素宽度 |
---|---|
640px | 100px |
480px | 75px |
320ox | 50px |
下面的表格是通过我们的元素在不同屏幕宽度下的计算值
页面宽度 | html字体大小 | p元素宽度 |
---|---|---|
640px | 640/100 = 6.4px | 15.625*6.4=100px |
480px | 480/100=4.8px | 15.625*4.8=75px |
320px | 320/100=3.2px | 15.625*3.2=50px |
可以发现,两边得出的元素宽度是一致的
上面的计算过程有些繁琐,开发的时候可以通过less预处理或者postcss插件来简化过程
3.2 vw,vh布局
|
|
聪明的你也许已经发现,这就是上面说到的单位x吗,根据定义可以发现1vw=1x,有了vw我们完全可以绕过rem这个中介了,下面两种方案是等价的,可以看到vw比rem更简单,毕竟rem是为了实现vw
|
|
虽然vw优点很明显,但是vw也有缺点,首先vw的兼容性不如rem好。
兼容性 | IOS | 安卓 |
---|---|---|
rem | 4.1+ | 2.1+ |
vw | 6.1+ | 4.4+ |
4. lib-flexible 实现
lib-flexible布局的前提就是viewport的scale根据devicePixelRatio动态设置:
在devicePixelRatio为2的时候,scale为0.5
在devicePixelRatio为3的时候,scale为0.3333
这么做目的当然是为了保证页面的大小与设计稿保持一致了,比如设计稿如果是750的横向分辨率,那么实际页面的device-width,以iphone6来说,也等于750,这样的话设计稿上标注的尺寸只要除以某一个值就能够转换为rem了。通过js设置viewport的方法如下:
|
|
第二个要点,就是html元素的font-size的计算公式,font-size = deviceWidth / 10。
|
|
接下来是,元素的尺寸该如何计算,比如说设计稿上某一个元素的宽为150px,换算成rem应该怎么算呢?这个值等于
设计稿标注尺寸/该设计稿对应的html的font-size。
拿淘宝触屏版来说的,他们用的设计稿是750的,所以html的font-size就是75,如果某个元素时150px的宽,换算成rem就是150 / 75 = 2rem。
5. 最后的最后
看一下在线的适配
|
|