为什么需要适配
目前市面上设备屏幕属性十分多样化(宽度和DPR并不一致),而作为设计和前端开发,无法为每个尺寸的设备单独设计一套UI并将其转为前端代码,这不现实。所以我们需要一套方案来将一套设计稿完美呈现在不同尺寸的设备上。
设备的多样性不止体现在设备屏幕尺寸上,还有屏幕的DPR也不同,有1,有2,有3等等。不同DPR的设备展示的视图清晰度也不同,所以这套方案要考虑屏幕尺寸和屏幕清晰度这两个因素。
一些基本概念
一个屏幕的DPR是根据屏幕的物理像素除以屏幕的设备独立像素(也叫密度无关像素)得来的(DPR = 物理像素 / 设备独立像素)。iphone6宽度的设备独立像素是375,宽度的物理像素是750,那么就意味着DPR是2。这就意味着需要四个物理像素来描绘一个设备独立像素,就像一个田(请忽略边框)的形状。
设备独立像素又叫做密度无关像素,可以认为是系统中一个点,这个点可以是可以由程序使用的虚拟像素(如CSS的px),然后系统会将这个像素转换为物理像素,在屏幕的DPR为2的设备上CSS的1px就会转为4个物理像素。
屏幕密度(PPI),通常是计算每英寸有多少个像素(设备独立像素),计算公式是:屏幕斜对角的像素数除以屏幕的英寸得出PPI的值。
对一开始适配问题做出一定程度的解释
根据上面DPR解释可以知道在DPR为2的屏幕上会使用四个设备像素来描述一个CSS像素。在这种情况下一张100*100的图片在页面上的尺寸也是100*100那么其实是200*200个物理像素来描绘这张图片,很明显图片的像素是不够的,这时候系统就会让剩下的像素就近取色,这样就造成了图片的模糊,想要图片不模糊要么提供一个200*200的图片,要么将页面上100*100的容器缩小到50*50。
在DPR为2的屏幕上高度的1px其实是由2个物理像素描绘的,所以该屏幕可以描绘的最小像素应该是0.5px,所以这个1px应该是0.5px才对。
怎么做适配
既然提出了问题那么就需要解决,解决方案主要来自手淘的flexibleJS,其原理就是适配不同屏幕尺寸主要用到了rem,而适配不同的DPR主要用到了<meta name="viewport" content="initial-scale=1/dpr"/>
不同屏幕尺寸
在CSS中单位使用rem,那么只要修改html的font-size那么这个页面的尺寸都会随之等比例改变。例如:
首先我们知道设计稿宽度例如750px,其中有个100px的正方形
其次我们也知道当前运行代码的设备的屏幕宽度document.documentElement.clientWidth
,这样就可以算出来在当前设备下这个正方形的宽高
通过观察上面设备下元素尺寸的计算公式 100px * document.documentElement.clientWidth / 750
。可以简化成 设计稿元素尺寸 * 某个系数
,可以联想到rem。这个单位后面的系数就是rem对应的font-size值。
所以在375屏幕尺寸下rem对应的font-size就是 375 / 750 等于 0.5px,0.5px * 100px 等于 50px。所以750下100单位在375设备下为50单位。
到这里我们得到公式 x = 元素在设计稿上的尺寸 * (设备宽度 / 设计稿宽度)
,其中 设备宽度 / 设计稿宽度
是html的font-size值,也就是1rem。
但问题是浏览器有最小font-size的限制,目前算出来的1rem对于的font-size是0.5px,显然小于最小font-size的限制。
所以我们要将1rem对应的font-size搞大点,修改下公式:
x = (元素在设计稿上的尺寸 / 100) * ((设备宽度 / 设计稿宽度) * 100)
将1rem对应的font-size变大之后,要将其调整回来,需要对设计稿上的尺寸再缩小对应倍数。
到这里我们就完成了一份设计稿适配不同尺寸设备的方案。
<div class='container'>
</div>
<style>.container {width: 1rem; /* (100px / 100)rem */height: 1rem; /* (100px / 100)rem */}
</style>
<script>
document.querySelector('html').style = 'font-size: ' + document.documentElement.clientWidth / 750 /* 设计稿尺寸 */ * 100 /* 将1rem对应的font-size值放大 */) + 'px'
</script>
不同DPR
模糊主要是由于图片像素不够导致的,例如有一张100 * 100的图片需要渲染在200 * 200的盒子中,因为图片的10000个像素点需要铺满40000个像素的面积,就会导致图片渲染的有些模糊,这个问题可以通过切2~3倍图来解决。但是0.5像素的问题则需要使用页面缩放来解决,因为dpr2的设备上可以渲染出来0.5px,而逻辑像素最小是1px。
对于适配不同设备DPR的高清方案思路如下:
- 将整个页面的基础单位,也就是rem放大 DPR倍
- 使用meta name=“viewport” 将整个页面缩小到 1 / DPR
这样就可以保证一个物理像素对应一个逻辑像素,让整个画面看起来清晰很多。
function adapterHD (width = 750) {const dpr = window.devicePixelRatio || 1document.querySelector('meta[name="viewport"]').setAttribute("content", "width=device-width, initial-scale=" + 1 / dpr)const oneRem = Math.floor(document.documentElement.clientWidth / width * 100)document.querySelector('html').style = 'font-size: ' + (oneRem) + 'px'
}
因为首先设置了缩放,缩放后 documentElement.clientWidth就会放大 DPR倍,所以onRem没有显式乘以DPR。
到这里还有两个问题需要解决:
- html元素被设置font-size之后因为font-size属性可以继承,整个网站的font-size默认都会变的很大,所以需要在body元素上重置下font-size属性
- 因为默认的font-size需要乘以100,所以在CSS中我们需要将设计稿上的尺寸除以100并且转换成rem单位,这个事情开发者手动去做十分繁琐且容易出错,所以可以用下面提到的postcss插件完成
flexibleJS
在引用上文提到的手淘的flexibleJS之后会自动设置html的font-size为75px,这时候的主要问题就是怎么将设计稿(一般都是750*1334)中的各个px值转换为rem。可以自己手动算(比较麻烦),可以使用编辑器插件,使用postcss插件等详情见使用Flexible实现手淘H5页面的终端适配
参考
移动端H5页面高清多屏适配方案
使用Flexible实现手淘H5页面的终端适配
viewports剖析
再聊移动端页面的适配
移动端Web页面适配方案