做移动端页面以来,经常会听说移动端的适配这个问题,但是并没有认真分析过是如何适配各种机型的。目前公司用的是手淘的 flexible.js 进行页面适配的。适配的根本原理其实就是将设计稿按一定的比例在不同的手机上实现。
在分析移动段适配之前首先要了解一下rem, css3 的一个相对长度单位。既然是相对长度,那就有一个参照体了,rem 就是相对于 html 元素的 font-size 计算值的倍数。即 1rem 等于一倍的 html 元素的 font-size 值。
接下来分析一下三种移动端适配的方法
一、@media + rem
最早看到这个适配是在同事的代码里,当时并不知到是什么原理,也并不明白这些数字是怎么来的。
@media screen and (min-width:350px){html{font-size:342%;}
}
@media screen and (min-width:360px){html{font-size:351.56%;}
}
@media screen and (min-width:375px){html{font-size:366.2%;}
}
@media screen and (min-width:384px){html{font-size:375%;}
}
@media screen and (min-width:390px){html{font-size:380.85%;}
}
@media screen and (min-width:393px){ /* 小米 NOTE */
html{font-size:383.79%;}
}
@media screen and (min-width:410px){html{font-size:400%;}
}
@media screen and (min-width:432px){ /* 魅族 3 */
html{font-size:421.875%;}
}
@media screen and (min-width:480px){html{font-size:469%;}
}
@media screen and (min-width:540px){html{font-size:527.34%;}
}
@media screen and (min-width:640px){html{font-size: 625%;}
}
@media screen and (width:720px){html{font-size: 703.125%;}
}
@media
媒体查询,可以针对不同的屏幕尺寸设置不同的样式,特别是如果你需要设置设计响应式的页面,@media 是非常有用的。当你重置浏览器大小的过程中,页面也会根据浏览器的宽度和高度重新渲染页面。
上述代码中,第一个 @media screen and (min-width:350px)
表示当移动设备的宽度大于 350px 的时候页面将使用花括号内的样式,即将 html 根元素的字号设置为 342%。(max-width:350px, 则表示设备宽度小于 350px 时将采用此样式)。上述 css 代码的作用可见就是在不同分辨率的设备上设置不同的 html 字体大小。
为什么要这样设置呢?因为这种适配方法用的是 css3 的 rem 来进行适配的,而前面讲了,rem 是相对于 html 的字号来计算的,现在不同的设备上 html 的字号改变了,也就意味这 1rem 代表的 px 像素值不同了,也就达到了按比例在不同设备上适配同一个页面的效果了。
html 元素的 font-size 值又是怎么确定的呢?拿下面的举例:
@media screen and (min-width:375px){html{font-size:366.2%;}
}
屏幕宽度大于 375px 的会按照宽度 375px 来适配。设计同时平时给我们的设计稿一般是 640px 宽度或者 750px 宽度的,而我们上面的都是假定设计稿是 640px 宽来计算的,750px 也是同理计算。现在:
1. 屏幕宽度是 375,设计稿宽度是 640,ratio = 375/640=0.5859375;
2. 我们要将设计稿上元素用 css 单位 rem 写下来,那么该如何转换,1rem 应该等于稿子上多少 px。
这里我们设定 1rem = 100px ; 可以设定其它值吗,当然可以,这里设置为 100 只是方便我们在写 css 的时候好计算,小数点直接左移两位就可以了。比如,设计稿上一个宽 46px 按钮,这样转换成 rem 直接就是 0.46rem。
3. 现在 1rem 代表设计稿上 100px, 那么又该是等于设备上最后真实的多少像素呢。就要用到前面的屏幕宽度和设计稿的宽度比 ratio, 设计稿上 100px 代表了真实的设备 100*ratio = 58.59375px。
换句话说 css 中写的1rem 等于设备 58.59375px。又因为 1rem 等于 1 倍的 html 元素的 font-size, 所以这里的 html 元素的 font-size 最终应该设置成 58.59375px。可为什么上述代码中用的是百分比呢?因为一般浏览器中 html 元素的默认字号都是 16px,但是当用户放大或者缩小浏览器字号设置时,就不会是 16px 了,所以我们将 html 的 font-size 还是设置成百分比更好,即 58.59375/16= 366.2109375%, 也就是上面例子中的 366.2% 了。
其它的屏幕上也是同此道理计算出 html 的 font-size 值的。
@media + rem 适配移动端还有一个不可少的条件就是要在 head 标签中写入一个 meta 标签。 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, minimum-scale=1">
; 关于 viewport 的了解可以看这里。此标签的作用是让 layout viewport = visual viewport, 用户也不可缩放页面。
二、手机淘宝 flexible.js
flexible.js 也是 rem 适配的,它是将设备分成 10 份,1rem 等于 1 /10。分析其中部分代码:
var devicePixelRatio = win.devicePixelRatio;
dpr = devicePixelRatio || 1;
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;
......
.......
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
win.devicePixelRatio
(简称 dpr), 即设备像素比(戳我了解)
上述代码当 dpr(设备物理像素和设备独立像素比)为 3 时候,页面缩入 1 /3,dpr 为 2 时,页面绽放 2 /1。
function refreshRem(){var width = docEl.getBoundingClientRect().width;
if (width / dpr > 750) {width = 750 * dpr;}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
上述代码将 1rem 设置成了设备真实宽度的 1 /10,因此 html 根元素的 fontSize 也就是设备真实宽度的 1 /10,假如设计老铁们给的漂亮稿子是 750px 宽的,写 scss 时 1rem 也就应该等于 75px, 那边我么的 scss 文件可以这样写:
@function px2rem($px, $base: 75) {@return ($px / $base) * 1rem;
}
/*
稿子上量得某按钮宽 60px, 高 20px
*/
.btn{width:px2rem(60);
height:px2rem(20);
}
三、vw,vh 进行适配
vw:viewport width(可视窗口宽度)
vh:viewport height(可视窗口高度)
vw 和 vh 等详情可以点这里
1vw 等于 1% 的设备宽度(设计稿宽度),1vh 等于 1% 的设备高度(设计稿高度), 这样看来 vw,vh 其它是最方便的,但是目前兼容性不是特别好。
所以只有在不需要考虑兼容的时候可以用这个相对最简便的适配方案了, 比如一些混合开发里,app 内的浏览器如果支持 vw、vh,只在 app 内使用的页面就可以放心大胆的用了。像下面的
客户端内的右下角 webview,一个小的 PK 对决页面,这里就是用的 vw,vh 进行适配的。
/* 右下角窗口设计稿宽 200px,高 220px*/
@function px2vw($px, $base: 200) {@return ($px/($base/100)) + vw;
}
@function px2vh($px, $base: 220) {@return ($px/($base/100)) + vh;
}
/* 头像宽 42px, 高 42px*/
.avantar{width:px2vw(42);
heightx:px2vh(42);
}
目前工作中用到的就是后面的两种适配方案了。手淘那个还有的地方看不懂,还是自己太 Low 了,但是看不懂车不影响老司机开车。