面试的时候被问到了,移动端的布局,一直没有总结,在这里做个总结,移动端的布局方案。
初始的移动端布局采用的是bootstrap的media query方案,后来演变到em,再到现在的rem,配合js,或配合vwscss,再加上flex box弹性盒子布局,还有正在酝酿的css grid方案,在实际工作中应该灵活应用,而不是一味的使用remflex等。
以前我们做的移动端适配就是设置meta标签

1
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1, minimum-scale=1">

然后,布局的时候使用bootstrap和Midea query配合来达到一个基本的h5的页面,这个方案在今天看来已经远远满足不了我们的需求。

em

在使用em作单位时,一定需要知道其父元素的设置,因为em就是一个相对值,而且是一个相对于父元素的值.

1
2
3
4
5
6
7
.parent{
font-size: 16px;
}
.child{
font-size: 2em; // 16px * 2
line-height: 2em; // 64px
}

em在做字体的单位时,相对于父元素font-size,而在做其他单位时,相对于的是自身的font-size大小

rem

rem则是相对于根元素html来进行计算的,如果子元素设置rem那么通过更改根元素html元素的字体大小,就可以让子元素的大小发生变化,这实际上也是一种等比缩放。

1
2
3
4
5
6
7
html{
font-size: 100px;
}
div{
width: 1rem;//100px
height: .5rem;//50px
}

那么,div的宽就是 100px 高是 50px
在实际工作中,可以使用js获取clientWidth来计算根元素htmlfont-size大小,使用scss来把 px 转换成 rem

rem 的使用方案一

js 只设置 html 的 font-size

前提是 假定 设计师 给我们的 图是 750 的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(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{
docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
}
};
if (!doc.addEventListener) return;
win.addEventListener(resizeEvt, recalc, false);
doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);

代码解释
如果页面的宽度超过了750px,那么页面中html的font-size恒为100px,否则,页面中html的font-size的大小为:100 * (当前页面宽度 / 750)

为什么 750px

这个要根据设计出的图了,这里还会涉及到一个dpr设备像素比的东西,这个在下文会有介绍。设计师如果是按照 iPhone6为原型出图的话,iPhone6的dpr2,它的屏幕宽度为375,那么一般出图都是 375 * 2 = 750的。

为什么是 100px

这是为了计算方便,设置为1rem = 100px后,页面的宽度就是7.5rem,这样如果一个输入框,设计师给我们的标注的高是 80px 的话,那么我们直接就可以除以 100,得出 0.8rem 了。这个做法也是网易的做法。

以 750px 为基准

屏幕宽度320480640750
屏幕对比比例0.4270.640.8531
html font-size42.7px64px85.3px100px
屏幕宽度7.4941rem7.5rem7.5029rem7.5rem

或者如果知道项目的主流媒体设备的话也可以使用media-query设置htmlfont-size但是这个方案不推荐

使用这个方案的话 meta 标签要这样设置

1
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1, minimum-scale=1">

这个方案所有设备不考虑dpr,会出现经典的1px边框的问题,对于要求严格的项目来说更适合下面这种方式。

rem的使用方案二

js设置 meta标签 viewport 配合scss

dpr

dpr(devicePixelRatio)称为设备像素比,每款设备的devicePixelRatio都是已知,并且不变的,目前高清屏,普遍都是2,不过还有更高的,比如2.5, 3 等。

  • 布局要点一: 动态的设置viewportscale等于 1 / dpr,设置页面缩放可以使得 1 个CSS像素(1px)由 1 个设备像素来显示,从而提高显示精度
  • 布局要点二: 动态的设置htmlfont-size为设备的宽度除以10,主流的把页面分成 10 份,那么750的设计图每一份就是75px,1rem = 75px;
  • 布局要点三: 使用scss计算rem
    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
    (function (doc, win) {
    var docEl = win.document.documentElement;// html
    var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
    var metaEl = doc.querySelector('meta[name="viewport"]');
    var dpr = 0;//初始化数据
    var scale = 0;//初始化数据

    //对iOS设备进行dpr的判断,对于Android系列,始终认为其dpr为1
    if (!dpr && !scale) {
    var isAndroid = win.navigator.appVersion.match(/android/gi);
    var isIPhone = win.navigator.appVersion.match(/[iphone|ipad]/gi);
    var devicePixelRatio = win.devicePixelRatio;//获得设备的dpr

    if(isIPhone) {
    dpr = devicePixelRatio;
    } else {
    drp = 1;
    }

    scale = 1 / dpr;
    }

    /**
    * ================================================
    * 设置font-size
    * ================================================
    */

    var clientWidth = docEl.clientWidth > 750 ? 750 : docEl.clientWidth;
    if(!clientWidth) return;
    docEl.style.fontSize = clientWidth / 10 + 'px';

    /**
    * ================================================
    * 设置data-dpr和viewport
    * ================================================
    */

    docEl.setAttribute('data-dpr', dpr);
    // 如果标签不存在append,如果存在改写meta:viewport标签
    if (!metaEl) {
    metaEl = doc.createElement('meta');
    metaEl.setAttribute('name', 'viewport');
    metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
    document.documentElement.firstElementChild.appendChild(metaEl);//添加到head标签中
    } else {
    metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
    }

    })(document, window);

rem 使用 scss 快速的把设计图上的标注转换为rem

使用scss直接把px转为 rem

1
2
3
4
5
6
7
8
$base-font-size: 75px;//根据项目具体设置
@function toRem($px){
@return ($px/$base-font-size) * 1rem;
}

p{
width: toRem(80px);
}

在实际项目中可以根据js来,动态的设置html的font-size,要注意的是,要把计算font-size的代码放到加载css代码之前,最好是内联。

文本单位

文本单位一般不建议使用 rem,建议使用px或em,使用px的话可以使用下面这个scss的混合宏

1
2
3
4
5
6
7
8
9
10
11
@mixin font-dpr($font-size){
font-size: $font-size;

[data-dpr="2"] & {
font-size: $font-size * 2;
}

[data-dpr="3"] & {
font-size: $font-size * 3;
}
}

使用

1
2
3
span {
font-size: @include font-dpr(16px);
}

vw 和 vh

详情请见我的另一篇博客 七个你可能没见过的css单位
上文中多次提到了这个 vw 它直接把浏览器分成了 100 份 , 100vw 就是 浏览器的宽度,那么 在设置 htmlfont-size 的时候,就可以直接设置,html:{font-size: 10vw}不需要使用 js 去设置根元素的font-size了,有个问题就是设置临界点,max-width 的问题,上面使用 js 设置的时候在设备宽度大于750的时候font-size就固定不变了,由于 dpr 的存在 这个时候应该访问 pc 端的设计图了,使用的时候也应该注意这一点。

总结

1.设置viewport标签的scale属性适配 dpr 不同的 设备

2.动态计算html的font-size,

3.对于固定宽度的元素使用 rem 布局,外层的容器元素使用 flex 布局

4.元素的font-size可能需要额外的媒介查询,并且font-size不使用rem,可以使用em

5.设置max-width,当宽度大于这个max-width的时候,左右居中。

6.推荐阅读 从网易与淘宝的font-size思考前端设计稿与工作流 中 手淘的 工作流程

参考链接