移动端开发的自适应

Posted by Nutlee on 2016-07-24

移动端开发进行大小、间距自适应一直是个不得不面对的问题。之前由于没有实战经验,以为只要注意使用rem和百分比进行大小设定就可以,实际开发中发现远没有这么简单。

rem

rem 是一个相对单位,即相对 HTML 元素的 font-size 的大小,这就使我们可以将 HTML 中所有元素的属性都是用 rem 来设置,再动态调整 rem 大小(即 HTML 标签上 font-size 值大小),整个网页就能动态适配了。
rem 需要 IE9 及以上的支持,考虑到只在移动端使用,这个兼容性要求还是相当可观的。

兼容性方案

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# 简单版
(function (doc, win) {
var docEl = doc.documentElement,
resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
recalc = function () {
var clientWidth = docEl.clientWidth;
if (!clientWidth) return;
if (clientWidth>=750) {
docEl.style.fontSize = '100px';
} else {
// 可能需要四舍五入 Math.round
docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
}
};
if (!doc.addEventListener) return;
var timer = null;
win.addEventListener(resizeEvt,function(){
// resize 节流
clearTimeout(timer);
timer = setTimeout(recalc,200);
}, false);
doc.addEventListener('DOMContentLoaded',recalc, false);
})(document, window);
# 复杂版本
(function (win){
var doc = win.document,
html = doc.documentElement,
option = html.getAttribute('data-use-rem');
// if( option === null ){
// return;
// }
// defaut baseWidth : 750px;
var baseWidth = parseInt(option).toString() === 'NaN' ? 750 : parseInt(option);
// 比例系数
var grids = baseWidth / 100;
// 视口宽度
var clientWidth = html.clientWidth>baseWidth?baseWidth:html.clientWidth;
// set rem first
html.style.fontSize = clientWidth / grids + 'px';
// create testDom
var testDom = document.createElement('div');
var testDomWidth = 0;
var adjustRatio = 0;
testDom.style.cssText = 'height:0;width:1rem;';
doc.body.appendChild(testDom);
var calcAdjustRatio = function(){
testDomWidth = testDom.offsetWidth;
if( testDomWidth !== Math.round(clientWidth / grids) ){
adjustRatio = clientWidth/grids/testDomWidth;
} else {
adjustRatio = 1;
}
doc.body.removeChild(testDom);
};
// 计算偏差比
setTimeout(calcAdjustRatio, 20);
var reCalc = function(){
var newCW = html.clientWidth>baseWidth?baseWidth:html.clientWidth;
if ( newCW === clientWidth ){
return;
}
clientWidth = newCW;
html.style.fontSize = newCW * adjustRatio / grids + 'px';
};
// 如果不能监听 立即调整
if (!doc.addEventListener){
reCalc();
return;
}
var resizeEvt = 'orientationchange' in win ? 'orientationchange' : 'resize';
var timer = null;
win.addEventListener(resizeEvt, function(){
clearTimeout(timer);
timer = setTimeout(reCalc,200)
}, false);
// detect clientWidth is changed ?
doc.addEventListener('DOMContentLoaded', reCalc, false);
})(window);

如上js代码中,设计稿是750px宽度,即对设计稿而言 1rem = 100px 。这样在根据设计稿切页面时,Chrome中自定义一个750宽度的页面,然后所有的关于大小、间距的值都根据 1rem = 100px 这个公式使用rem为单位设置即可。

注意:

  • 并不是在 css 中每个大小、间距的值都适合使用rem为单位,如分割线、border 等可能需要在不同页面中保持完全一样的效果,就需要写死 px,所以具体开发中还是要灵活考虑。
  • Chrome 中最小字号为12px,虽然可以使用一些手段突破这个限制,但不建议大面积这样操作。所以需要考虑本来就很小(12px)的字,无法进一步自适应的情况。适当的对小字符写死也是个不错的选择。
  • css 中 font-size/border 只能为整数,而 width/height/margin/padding 却可以设置为小数,所以有时会出现用 border 画出来的图形和使用 width/height 画出来的图像不对齐等现象,这时候适当取整 rem 对应的 px 值可以解决。
  • 部分浏览器对 rem 的解析会有偏差,所以增加复杂版本创建一个 1rem 的容器去探测这个偏差比来修正。

参考资料:
web App变革之rem