前置的一些概念,可以看这两个文章:
使用Flexible实现手淘H5页面的终端适配
flexible 详解
因为之前接触了很多适配方案,大同小异,导致对rem适配方案始终无法形成有效的记忆。
这次,直接拿手淘的flexiable 作为标准方案,理解它,作为自己的适配方案。
看flexible关键代码:
找到一个设备的dpr
//
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;
} else {
dpr = 1;
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}
devicePixelRatio
该 Window 属性 devicePixelRatio 能够返回当前显示设备的物理像素分辨率与 CSS 像素分辨率的比率。此值也可以解释为像素大小的比率:
一个 CSS 像素的大小与一个物理像素的大小的比值。简单地说,这告诉浏览器应该使用多少个屏幕的实际像素来绘制单个 CSS 像素。
找到一个设备的css宽度(逻辑宽度,视口)
var width = docEl.getBoundingClientRect().width;
getBoundingClientRect
Element.getBoundingClientRect()
方法返回元素的大小及其相对于视口的位置。
如果是标准盒子模型,元素的尺寸等于width/height
+ padding
+ border-width
如果box-sizing: border-box
,元素的的尺寸等于 width/height
。
找到一个设备的 rem
function refreshRem(){
var width = docEl.getBoundingClientRect().width;
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
width / dpr
得到 1 个 dpr 的物理像素540
限定,540*dpr 是最大的适配宽度,避免图片模糊;var rem = width / 10;
把屏幕宽度 分为 10 份,1份1rem;(直接分100份,有1rem 小于12px 的风险)
设计约定俗成的一个前提
设计师一般都是以iphone6 为基础设计UI,iphone6 css width 是375px;设计稿为两倍图 750px;
(设计师为了出高清图,一般稿子都会出到符合物理像素的大小)
开发一份基于iphone6的UI布局实现
假如设计稿上有个div ,测量是 100*100
因为设计稿总宽度是750px, 分为10分, 每份就是1rem,即是75px; 100/75 = 1.3333 rem
代码就是:
div{
width: 1.3333rem;
height: 1.3333rem;
}
这是直接通过rem单位实现了设计稿的尺寸。如果直接放到iphone6 上,会发现,页面比屏幕大了一倍;因为设计稿是按照逻辑像素1:1出的,此时 1px = 1pt。而iphone6 的css是 1px = 2pt 所以我们还需要对页面缩放 scale
找到一个设备的 scale ,屏幕缩放比
dpr = window.devicePixelRatio
dpr = 物理像素 / 逻辑像素
scale = 1 / dpr;
所有设备的设计稿实现都缩小为 1个逻辑像素屏幕 大小;
这时候,UI 可以在iphone6 的屏幕上完美呈现;
其它机型怎么适配?
你已经得到一份基于iphone6 的 rem 单位实现;
因为rem 是相对单位,并且是约定把1屏宽度分为10份来实现的rem;换句话说就是,所有1rem为1屏的十分之一的手机,显示这套实现都不会布局错乱。所以这套实现是可以直接复用的。
要复用,需要满足你的html 设置满足一些条件
复用条件
1:把屏幕分为10份,每一份 表示1rem,设置 html: font-size = (width/10) px
2:把视口缩放为 (1/dpr)
大白话
大白话讲,我们目的是为了 把一份基于rem的实现 放到所有的手机屏幕上,让他们都显示一样,只是等比缩放的差异而已;
通过viewpoint 的 scale 属性,我们可以把html 像缩放图片一样,缩放到刚刚好100%屏幕。
通过设置1rem 对应十分之一屏幕,我们可以复用这份布局实现。
后续
因为viewpoint 再浏览器的完善,flexible.js 已经推出2.0版本;
2.0
看了下关键源码:
// adjust body font size
function setBodyFontSize () {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
}
else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
// set 1rem = viewWidth / 10
function setRemUnit () {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})
大概思想还是把1屏分为 10等份;不过再使用rem 作为单位了,而是使用vw
vw 天然就是把1屏幕 = 100 vw;
然后上面的那套iphone6实现,把所有rem 改为vw 就可以了。