序言
移动端屏幕尺寸千奇百怪,因此H5页面的适配对每一个前端工程师来说,都是比较头疼的问题。网上有很多关于移动端适配的解决方案,但都不是很全面。flexible是阿里手淘前端团队用于解决移动端页面适配的方案,经过时间和各种项目的检验,是我认为的一个比较成熟的解决方案。
flexible是什么
lib-flexible
是一个js库,用于动态计算与设置页面的缩放比例和html的font-size,实现移动端适配。
源码
1 | ; |
源码(压缩版)
1 | <script>!function(e){function t(a){if(i[a])return i[a].exports;var n=i[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var i={};return t.m=e,t.c=i,t.p="",t(0)}([function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=window;t["default"]=i.flex=function(normal,e,t){var a=e||100,n=t||1,r=i.document,o=navigator.userAgent,d=o.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i),l=o.match(/U3\/((\d+|\.){5,})/i),c=l&&parseInt(l[1].split(".").join(""),10)>=80,p=navigator.appVersion.match(/(iphone|ipad|ipod)/gi),s=i.devicePixelRatio||1;p||d&&d[1]>534||c||(s=1);var u=normal?1:1/s,m=r.querySelector('meta[name="viewport"]');m||(m=r.createElement("meta"),m.setAttribute("name","viewport"),r.head.appendChild(m)),m.setAttribute("content","width=device-width,user-scalable=no,initial-scale="+u+",maximum-scale="+u+",minimum-scale="+u),r.documentElement.style.fontSize=normal?"50px": a/2*s*n+"px"},e.exports=t["default"]}]); flex(false,100, 1);</script> |
flexible是如何实现页面适配的
适配的基准:设备像素比(device pixel ratio)
这里涉及到一个很重要的概念即设备像素比
,简称dpr。计算公式为设备像素比 = 物理像素 / 设备独立像素
。
物理像素(physical pixel)
物理像素又称设备像素,指的是设备中使用的物理像素。
比如iPhone 6的分辨率为1334x750像素。
设备独立像素(density-independent pixel)
独立于设备的用于逻辑上衡量像素的单位。这是一个总体的概念,包括了CSS像素。
在JavaScript中,可以通过window.devicePixelRatio
获取到当前设备的dpr。
在CSS中,可以通过-webkit-device-pixel-ratio
,-webkit-min-device-pixel-ratio
和 -webkit-max-device-pixel-ratio
进行媒体查询,对不同dpr的设备,做一些样式适配(这里只针对webkit内核的浏览器和webview)。
dpr对页面显示效果的影响
对比Retina屏(dpr = 2)和普通屏(dpr = 1),css像素所呈现的大小(物理尺寸)是一致的,不同的是1个css像素所对应的物理像素个数是不一致的。
在retina屏幕下,1个css像素对应1个物理像素(1:1)。 在普通屏幕下,1个css像素对应4个物理像素(1:4)。如图:
理论上,1个css像素对应于1个物理像素,才能得到完美清晰的展示。
因此对于dpr = 2的屏幕,需要将屏幕缩放到原来的1/2才能达到最完美的显示效果,dpr = 3,则需要缩放为原来的1/3。
常见手机的dpr
iPhone 4以上都是用的是Retina屏。
其中4, 4s, 5, 5s, 6, 6s的dpr都是2,6+和6s+的dpr是3。
该方案中并未对安卓设备进行适配,默认dpr均为1。主要原因有如下几点:
- viewport content 的写法要兼容各种奇葩 Android 设备,兼容测试的成本很高
- 部分机型修改viewport之后产生屏幕抖动
- 部分机型 WebView Width 与屏幕实际宽度不一致
- 还有很多无法预料到的奇葩BUG存在
通过dpr动态设置相关属性
根据dpr设置页面缩放比例
1 | ... |
这里涉及到了一个东西叫viewport
。
一般移动设备的浏览器都默认设置了一个viewport
元标签,定义一个虚拟的layout viewport
(布局视口),用于解决早期的页面在手机上显示的问题。
移动设备的浏览器就是把页面放在一个虚拟的“窗口”(viewport)中,窗口可大于或小于手机的可视区域,一般手机默认viewport大于可视区域。这样不会破坏没有针对手机浏览器优化的网页的布局,用户可以通过平移和缩放来看网页的其他部分。如图:
一般移动设备浏览器的viewport
的width
默认为980px,所以我们只能看见部分区域的页面,需要滚动页面才能看见其他部分。所以我们需要将width = device-width
,将可视窗口(viewport)的宽度设为当前设备的宽度,然后将initial-scale = 1
,保持原来的比例。就能把一个页面适应的移动设备上。
一个常见的viewport
类型的meta
标签:
<meta name=”viewport” content=”width=device-width, initial-scale=1, maximum-scale=1″>
根据dpr设置html的font-size
此方案默认以640px的设计稿为标准。因此dpr = 2时,html
的font-size
为100px。依此类推。
如何使用flexible
在页面head
标签里引入第一节中的js代码。页面渲染之前会执行该代码,给页面设置好缩放比例和html的font-size。
然后我们根据设计稿(通常以640px为基准,1rem = 100px),通过rem设置相应元素的宽高即可。
tip:貌似flexible已经发布了2.0版本了,是基于vw方案(Github地址),下回分解吧- -。相比之下两个方案各有利弊吧,都可以解决移动端适配的大部分问题了,感谢大佬们的贡献!