移动前端适配(二)实践篇

继续上一篇的内容,这里重点说说通过 rem 来做好移动端的适配。

移动前端的开发,先来熟悉一些基本的东西:

rem 简介

rem(font size of the root element):相对于根元素字体大小的单位;
即,通过 px 设定 html 的 font-size 值,然后其它元素的长宽全部通过 rem 来设置。
计算公式:
实际显示的大小值 = rem值 * 根元素值
比如:

1
2
3
4
5
html{font-size: 20px;}
/*rem设置*/
div{width:8rem; height:4rem; font-size:1.5rem; border-radius:5rem;}
/*实际显示的大小*/
div{width:160px; height:80px; font-size:30px; border-radius:100px;}

旧版自适应

我们知道,meta 标签的 maximum-scale 属性可以设置页面的最大缩放;
旧版的自适应的思路:

  1. 动态获取当前移动设备的屏幕大小;
  2. 根据当前屏幕大小去计算出缩放的比例;
  3. 将该比例通过 js 动态写入 meta 标签。

即:

1
2
var phoneScale = parseInt(window.screen.width)/640;
document.write('<meta name="viewport" content="width=640, initial-scale = '+phoneScale+', maximum-scale = '+phoneScale+',target-densitydpi=device-dpi">');

这种方式,可以根据不同的屏幕大小、完全按照设计图的比例来缩放;操作简单,所有元素的大小设置权威 px,基本跟平常的 PC端没什么区别。
但是,该方式简单粗暴,会使得页面失真严重、变得模糊。

新版 rem 自适应

项目的新版自适应方式,是以 rem 为基础的,前面已经对 rem 有所介绍了。

基本思路

新版自适应的基本思路:

  1. 根元素 html 的 font-size值 的设置;
  2. 其它所有元素的大小,按照设计图实际大小、用 rem 来设置;
  3. 根据屏幕大小,通过 js 动态修改根元素的 font-size值,到达适配所有移动设备。

实际应用

我们根据上面的基本思路、来走一遍:

根元素的确定

根元素的确定,主要是因为浏览器能识别的最小值为 12px,当值小于 12px 全按照 12 px来渲染。

假设 html 下font-size 值设置为: font-size:20px,会出现一下问题:
假设元素总体长度最大为: 640px/20px=32rem,最小为: 320px/20px=16rem;
当设备屏幕缩小时、js 动态等比改变 html 下font-size 值大小;
假如当设备宽度减少到 384px 时,此时 html 下font-size 值大小为 384px / 640px * 20=12px(chrome最小能识别);
但是,元素最小宽度是: 320px/20px=16rem,320px < 384px 呀,所以当屏幕在 320px~384px 范围时,font-size 值一直只能按照12px,因此元素就再也不会缩放适应了、进而出现横向滚动条。

最终确定的重新设置为 font-size:40px,
假如当设备宽度减少到 320px 时,此时 html 下font-size 值大小为 320px / 640px * 40=20px;
此时,20px,还没到达最小值 12px,仍可缩放;且 320px 为最小宽度 min-width 了,因此 font-size:20px; 就是最小的值了,它大于 12px。

rem设置元素

除了根元素font-size是 px 设置的大小,其它所有元素均用 rem 来设置大小;
比如,一个 div 在 ui设置图的实际大小是 640px ,那么它用 rem 来设置时是:16rem( 640px/40px = 16rem );

1
2
3
4
html{font-size: 40px;}
/*rem设置*/
div{width:16rem;}
......

动态修改根元素大小

现在,所有用 rem 设置大小的元素,都是以根元素的 font-size值为基准的;当我们只是修改根元素大小时,那么所有 rem 实际显示的大小都会改变,从而达到适配所有屏幕大小。

动态修改根元素大小的方式有 2 种,一种是 css媒体查询断点修改
,一种是 js 动态修改

一、css媒体查询断点修改,针对特定的几种常用的屏幕大小、动态修改根元素的 font-size值,具体的计算方式为:
屏幕大小 / ui实际大小 * 原始根元素大小 = 动态根元素大小
比如第一种情况:当屏幕大小为345px,ui实际大小为640px,原始跟元素大小为 font-size:40px 时:
345 / 640 * 40 = 20px
具体看以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
html{font-size: 40px;}
@media (min-width:320px) and (max-width:345px){
html{font-size: 20px;}
}
@media (min-width:346px) and (max-width:360px){
html{font-size: 22.5px;}
}
@media (min-width:361px) and (max-width:375px){
html{font-size: 23.4375px;}
}
@media (min-width:376px) and (max-width:420px){
html{font-size: 26.25px;}
}

二、js 动态修改根元素大小,不是断点式的,而是连续的,即所有的屏幕大小都能做到自适应。
这里主要依赖一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*pageFit start*/
function pageFit(doc,win,maxwidth,minwidth,font) {
var docEl = doc.documentElement,
resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
recalc = function () {
var clientWidth = docEl.clientWidth;
if (!clientWidth) return;
//最大最小width范围内,自由缩放
if(clientWidth>=minwidth&&clientWidth<=maxwidth){
docEl.style.fontSize = font * (clientWidth / maxwidth) + 'px';
}
//超出最大宽度,误差校准
else if(clientWidth>maxwidth){
docEl.style.fontSize = font+'px';
}
//小于最小宽度,误差校准
else if(clientWidth<minwidth){
docEl.style.fontSize = font * (minwidth / maxwidth) + 'px';
}
};
if (!doc.addEventListener) return;
win.addEventListener(resizeEvt, recalc, false);
doc.addEventListener('DOMContentLoaded', recalc, false);
}
pageFit(document,window,640,320,40); //最大宽度maxwidth,最小宽度minwidth,html下的font-size
/*pageFit end*/

最后,附上该项目的线上地址: 项目地址