话说我刚工作的时候,就开始用rem了,过了没多久,接触到了flexible,系统化且支持iOS的retina屏迅速征服了我,最近又看到了大漠大神的vw。所以本文想完成一篇一站式的文章,可以系统的了解前端适配的演进。闲话少叙,马上开始。
1. 什么是前端适配
从UI展现层面上:
我们期望不同尺寸的设备,页面可以
自适应的展示
或者进行
等比缩放
,从而在不同的尺寸的设备下
看起来协调
或者
差不多
。
从代码实现层面上:
我们希望前端适配可以用用
尽可能简洁
的代码来实现。最好
一套代码
实现兼容所有设备,而不是对每个或每种设备都写一套方案,不是次次都选用最无奈的方案(Android和iOS分开编写)。
2. 关键字
如果你了解这些关键字,那么这段大可以跳过,如果后面遇到了问题,感觉有些疑惑,也可以再回来查阅。
2.1 Viewport/视口
通俗的讲,移动设备上的viewport就是设备的屏幕上能用来显示我们的网页的那一块区域1,但不一定是我们可见的区域。具体来说,分为以下三种。
2.1.1 Visual Viewport
Visual Viewport: 可见视口。就是移动设备上可以被我们看见的部分。宽度在移动端通过window.innerWidth获得(仅限移动端,PC上哪怕是chrome模拟也会有不同的结果)。
2.2.2 Layout Viewport
Layout Viewport: 布局视口。
如果把PC上的页面放到移动端,以iphone8为例,如果只展示为可见视口的宽度(375px),那么页面会被压缩的特别窄而显示错乱,所以移动浏览器就决定默认情况下把viewport设为一个较宽的值,比如980px,这样的话即使是那些为桌面设计的网站也能在移动浏览器上正常显示了。1
而事实上,我们一般看不到如上图这样出现横向滚动条的界面;在手机上访问页面时,往往是下图的样子:
这是由于页面body宽度设置了100%而没有指定一个具体的宽度导致的,从而使页面被等比缩放了。由于用户可以缩放,所以还算能正常浏览。
2.2.3 Ideal Viewport
Ideal Viewport:理想视口,其实就是设备的可见区域,和可见视口一致。
设置Ideal Viewport的好处是,只要按照Ideal Viewport来设计样式稿,用户就不用能最完美的查看网站的内容——既不用左右滑动,也不用放大缩小。
设置理想视口:
name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
这段代码的意思是将布局视口的宽度设置为设备宽度,初始缩放比例为1,最大缩放比例为1,用户不能缩放。
2.2 像素
2.2.1 物理像素
物理像素:一个物理像素是显示器(手机屏幕)上最小的物理显示单元,在操作系统的调度下,每一个设备像素都有自己的颜色值和亮度值。2
2.2.2 设备独立像素
设备独立像素:又称为CSS像素,就是我们日常代码中使用的像素。浏览器内的一切长度都是以CSS像素为单位的,CSS像素的单位是px。
2.2.3 设备像素比
设备像素比(简称dpr)定义了物理像素和设备独立像素的对应关系。比如说对于iOS的retina屏,一个设备独立像素就对应着4个物理像素。这样的设计可以使画面更加清晰锐利,如下图:
3. 业界的解决方案
OK,LongLongAgo的前缀之后,终于到了正题。回到我们最开始的初心:我们只是想要通过一套代码,实现一个可以在不同页面尺寸上展示差不多的页面。在这一块,现在主要有三种方案。
3.1 Rem的解决方案
DPR一致时,px在不同的屏幕尺寸上会展示为定宽,这就导致我们的页面可能会出现滚动条或者占不满的情况。而通过rem来设置div的宽高,可以保证页面可以通过调整html的font-size来整体放大或者缩小,从而达到不管屏幕宽度是多少,页面都能完美展示的效果。
例如,针对750*1334的设计稿:
name="viewport" content="initial-scale=1,maximum-scale=1, minimum-scale=1">
document.documentElement.style.
fontSize = window.innerWidth / 7.5 + 'px';
这样,所有的设备的宽度都是7.5rem,只需要把设计稿上的px值统一除以100,就可以得到相应的rem值了。
网易也采用的这种方法。
3.2 Flexible.js
Flexible是阿里团队开发的前端适配方案,也是用的rem的方法。那么第一种方法其实已经能解决前端适配问题了,为什么阿里还要开发一个Flexible呢?
在第一种方法中,dpr=1时没有任何问题,但是在dpr=2或者更高的手机屏幕上,因为物理像素的增加,存在小于1px的显示空间。如果采用第一种方法,因为它统一对scale设置为1,那么我们假如想要实现0.5px, 就只能通过transform的方式。如果有多个这样的样式,代码就会变得很麻烦。
.scale{
position: relative;
}
.scale:after{
content:"";
position: absolute;
bottom:0px;
left:0px;
right:0px;
border-bottom:1px solid #ddd;
-webkit-transform:scaleY(.5);
-webkit-transform-origin:0 0;
}
因此,阿里的flexible方案充分考虑了这种情况,动态的设置了fontsize和scale, 从而使得CSS中的1px等于物理像素中的1px,在IOS下得到最清晰的体验。
if (!dpr && !scale) {
var isAndroid = win.navigator.appVersion.match(/android/gi);
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
dpr = 2;
}