精通移动端布局 - 实践篇 -

本文大多数的内容基本都是从多篇博客或相关文章中进行筛选,提炼出来,原本我也想用我匮乏的语言来描述,但是发现别人已经总结的更好了,所以...我还是乖乖的站在巨人的肩膀上吧~~


完整目录:

基本概念
    物理像素
    设备独立像素
    CSS 像素
    PPI的概念
    DPR的概念
    缩放的概念
    viewPort 的概念
    viewport 渲染流程
    Meta 标签说明

移动端布局实践
    混合方式
    REM 方式
        响应式
        JS自动换算
    缩放方式
        CSS3 缩放
        viewport 缩放

相关补充
    为什么需要meta标签?
    传统响应式布局与移动端响应式的区别
    移动端字体以及大小的设置
    移动端背景图缩放设置
    使用Sass提高px与rem转换效率
    通过Chrome进行真机调试
    weinre 远程调试


移动端布局实践

混合方式

混合方式实现的移动端布局实际上就是对PC端布局技术的组合使用,它主要包含以下技术:

  • 定位布局
  • 浮动布局
  • 可塑性布局
  • 百分比布局
  • 相对单位

将这些布局方法根据页面每个不同部分的特性进行组合使用,例如模态框,弹出层等可以采用定位方式,而页面元素的排版可以采用浮动布局,也可以使用具有可塑性的Flex布局方案,对于元素的尺寸可以采用百分比或者其它相对单位,而这便是我称之为“混合方式”的原因。
混合方式进行的移动端网页布局,所采用的技术通常都具有灵活、可伸缩、可塑等特点,甚至连使用的单位,最好都是相对单位,也只有这样才能满足屏幕尺寸多样性的移动设备。

对于混合布局技术,我首先要说几点我认为比较重要的基础知识点:

· 声明meta标签
通过meta标签来声明viewport是进行手机端页面开发的必须也是起始步骤

<meta name="viewport" content="width=device-width,maximum-scale=1,minimum-scale=1,user-scalable=no />

· 百分比计算公式
如果你在进行移动端网页布局中使用到了百分比单位,那么这个公式你一定要清楚

百分比 = 目标尺寸 / 上下文尺寸。

其中目标尺寸就是当前元素的尺寸,而上下文尺寸则是包含当前元素的父元素,通过 目标尺寸 / 上下文尺寸 便可以得到我们需要的百分比结果,另外个人建议百分比结果保留所有小数。

· 设置阀值
可伸缩的布局方案有一个本质特点就是会根绝显示设备的尺寸进行自适应调整,这一特性本身就是我们所期待的,但是在某些极端的环境下,比如过大或过小的屏幕下,某些元素尺寸也会变的过大或者过小,或者图片被严重拉伸,导致失真等,从而使显示效果变的很差,而下面的这些CSS属性就可以很简单解决这些问题。

max-width:  //设置最大宽度阈值
min-width:  //设置最小宽度阈值
max-height  //设置最大高度阈值
min-height  //设置最小高度阈值

在使用上可以将它们作用在目标元素上,或者对目标元素有所限制的上级元素。

· 对于单位的认识
对于网页布局的单位,我们不仅要知道px就够了,还要知道其它几个比较常用的单位,特别是CSS3推出的一些新的度量单位。

em
em 是一种相对单位,它相对于父元素的字体大小。
em 常用于可缩放的字体需求中,比如在一个多行段落文本中,如果我们设置行高为 line-height:16px 这一固定的像素值,那么一旦在缩放或者改变了文字的大小的情况下,字体会发生大小变化,但是行高的值却不会发生改变,永远为16px,如果我们设置行高为 line-height:1em 那么此时行高的值便是一种相对的情况,它会随着文字大小的改变而改变。
因为em是相对于父元素的字体大小,所以在使用上我们通常都是为 body 设置字体大小,这样便可以实现一改全改的效果。另外我们还需要了解的是,浏览器默认的字体大小是16px,因此 1em 也就等于 16px。

rem
rem是一种相对单位,它相对于根元素 html 的字体大小。
利用这一特性,rem常用于移动端页面布局,下文中我们会更详细的说到。

vh/vw
vh/vw都是相对于视口的单位,浏览器视口的区域就是通过 window.innerWidth以及 window.innerHeigth 度量得到的范围。
浏览器会将整个视口的宽度或者高度划分为100等份,因此1vw或者1wh就是相对视口宽度或者高度的1%。例如浏览器视口的宽度是1920,那么 1920/100 每等份即19.2px。
vh/vw 在使用上很类似与百分比,但是vh/vw也具有百分比不具有的特性,那就是相对于视口,例如当父级元素不具有尺寸的时候,百分比便无法产生作用,但是相对于视口的vh/vw则可以。
vh/vw 还可以应用在一个页面上有很多垂直排列的区块,而且每个区块的高度都是当前浏览器显示区域高度的情况下。

vmin / vmax
vmin / vmax 也是相对于视口的单位,但是vmin会根据视口宽高最小的那个为基准,然后分为100等份,而vmax 则会以视口宽高最大的那个一个为基准,然后分为100等份。

在了解了以上的基本知识后,我会根据一个现成的示例去解读混合方式的使用实例,这里我就以手机新浪网为例。
首先打开手机新浪网:https://sina.cn/,在chrome下按 F12 ,从上至下观察,我们可以看到,新浪手机版页面的head中,通过meta标签对viewport的尺寸以及缩放进行了调整,再大致的看下页面内容我们可以看出手机新浪网上很好的运用了H5新增的语义化标签,例如:

section : 定义每个区域
details : 设置display:none隐藏,来表述对区域内容或作用的声明
mark    : 标记内容,比如广告等。
nav     : 用作导航
aside   : 用于表示与页面主内容松散相关的内容,经常用于侧边栏、广告、友情链接、引文以及导航元素等。
header  : 定义头部区域
footer  : 定义了网页的尾部,它主要包含联系方式、地址、备案信息等。

再从整体到细节,首先是banner区域,我们会发现banner区域的HTML结构很简单,就是 div > a > img + mark 结构,而且img的宽度是100%即当前显示区域的100%,高度则是自动适应。

banner

接着,我们看手机版的头部,它位于banner的下面,主要由logo、两个按钮以及常用功能按钮(天气,登录)等组成,整个布局的思路是左右结构,logo与两个切换按钮都是左浮动,而常用功能按钮则是采用右浮动。这样的好处就体现于在左右两个结构之间留下了足够多的空白区域。
再接着的便是导航区域,导航的按钮数量很多,但是结构依然很简单,就是 nav > a 结构,并且所有的 a 进行左浮动,因为 a 中的内容都是文字,所以每个导航按钮的高度可以通过 line-height 进行控制,每个按钮之间的间隔则用 padding 撑开,宽度则是根据一行要展示的按钮数量除以1,这里一行显示6个再考虑到间距,因此宽度都是15%,采用百分比单位,最后通过 white-space:nowrap 强制控制按钮内容不换行。

nav

再往下便是焦点图区域,这个区域跟我们平常编写焦点图基本相似,所以就不在赘述。
最后便是页面主要的资讯内容区域,通过控制台我们可以得到内容区域的主要结构是 dl > dt + dd 并且 dl 应用了 display:flex,所以dl中的dt与dd便会水平并排自动划分宽度排列。dt中的是新闻图片,而dd中则存放新闻标题以及icon,而且dd也应用了box特性,并通过以下CSS属性,设置其内容及子元素的水平以及垂直排列方式

-webkit-box-orient:vertical; // 设置内容或子元素为上下垂直排列
-webkit-box-pack:justify     // 设置内容或子元素为两端排列

zw

总的来说,混合模式在使用上其优点在于技术上接近传统PC端页面的布局方式,而你所要做的只是对PC上的布局技术进行稍微的改变,但是其缺点也很明显那就是关键元素高宽和位置都不变,只有容器元素在做伸缩变换,所以对于混合模式下的布局,元素、图片、文字在设计的时候都 要满足以下特性:

·弹性的元素控件
·自适应的图片尺寸
·流式的文字展示

更直观的理解,可以看下图:

设计稿的特点

REM 方式

REM 是一种相对单位,它依据的是“根”节点的字体 (font-size) 的大小。
REM 是目前移动端页面布局常用的方式,它可以使整个页面的所有元素都随着显示设备尺寸的变化而相应的调整自己的尺寸,从而增强页面对设备适配的灵活性,这种变化我称之为“页面的缩放”。
既然REM会让页面根据不同的显示设备进行适配缩放,那么必然就会有一个 标准页面尺寸,就目前而言,整个前端开发界使用最多的标准页面尺寸则是根据iphone4或者 iPhone5为依据的 640px*1366px ,也有以iphone6为基准的750px。这个标准的页面尺寸,我们可以将其定义为1,如果当前的显示设备尺寸小于标准页面尺寸(640px或者750px)那么便让页面尺寸缩小,使其小于1。而当显示设备尺寸大于标准页面尺寸,我们即可以做一些其它的适配,也可以将页面整个居中显示在显示设备中然后不进行任何缩放操作。
通过上述,我们可以知道两个关键点,一是页面的缩放,这个我们等下会说到,二就是采用rem为单位进行页面布局,实际上rem布局与px布局并没有什么本质的区别,这个我们可以代入实例去理解,比如现在 html 的 font-size的大小是100px,即 1rem = 100px,如果现在页面中要放入一个200*200的盒子,那么按照等比关系,即:

div{
    width:2rem;
    height:2rem;
    background:gray;
}

除了将rem与px结合起来进行理解rem布局的原理,淘宝的前端工程师也提出了另一种理解方式,这种方式的优点在于可以向后与 vw/vh 进行兼容理解,首先将页面划分为100分,每一份的宽度为(n)px,设定1rem = 10n,如果基准页面是640px的话,那么每份即6.4px 而 1rem = 64px。如果现在有一个 200px * 200px 的盒子,那么按照rem与px的等比关系,即:

div{
    width:3.125rem;
    height:3.125rem;
    background:gray;
}

最后我们谈谈 REM 实现的页面缩放适配原理, rem 是依据 html标记的 font-size 大小的相对单位,对于使用rem为单位的页面,在被载入到显示设备显示的时候,会根据显示设备的尺寸,然后对应的修改html标签的font-size值,这样便可以一处修改,整个页面内容都会发生改变,即实现根据设备尺寸进行缩放的效果。
REM方式进行移动端布局的原理都是相同的,但是不同的在与对于设备尺寸的检测上,就目前而言分为两种,一种是通过CSS meida查询,另一种则是通过JS检测。

响应式

响应式方式就是通过 CSS media 对显示设备进行媒体查询来改变rem与px的对应关系。
下面我贴出我己工作中使用到的移动端媒体查询规则:

@media screen and (min-width:320px) and (max-width:359px) {
  html {
    font-size: 50px;
  }
}
@media screen and (min-width:360px) and (max-width:374px) {
  html {
    font-size: 56.25px;
  }
}
@media screen and (min-width:375px) and (max-width:383px) {
  html {
    font-size: 58.59375px;
  }
}
@media screen and (min-width:384px) and (max-width:399px) {
  html {
    font-size: 60px;
  }
}
@media screen and (min-width:400px) and (max-width:413px) {
  html {
    font-size: 62.5px;
  }
}
@media screen and (min-width:414px) and (max-width:431px) {
  html {
    font-size: 64.6875px;
  }
}
@media screen and (min-width:432px) and (max-width:479px) {
  html {
    font-size: 67.5px;
  }
}
@media screen and (min-width:480px) and (max-width:539px) {
  html {
    font-size: 75px;
  }
}
@media screen and (min-width:540px) and (max-width:639px) {
  html {
    font-size: 84.375px;
  }
}
@media screen and (min-width:640px) {
  html {
    font-size: 100px;
  }
  body {
    max-width: 640px !important;
    margin: 0px auto !important;
  }
}

在上面这段CSS媒体查询代码中,我们以一个640px宽度的设计稿为基准的页面尺寸,并设定 html 标记的 font-size 值为100px,然后检测载入当前页面的显示设备尺寸,对应的改变font-size的值,最后再进行相应的缩放适配显示页面,当显示设备尺寸大于基准页面尺寸时则居中显示在显示设备中。
响应式REM布局的优点在于可以根据设计稿的特点,自定义的对某些设备进行单独适配,而缺点是检测规则固定不可变,这一点相比于“JS自动换算”更为明显。

JS自动换算

根绝响应式方式的REM思路,我们就可以很简单的通过JS代码写出相应的JS换算方式。
代码如下:

(function(win,doc){

    var timer = null,
        html = doc.documentElement,
        baseWidth = html.dataset.basewidth*1 || 640,
        metaEl = document.querySelector('meta[name="viewport"]'),
        event = 'onorientationchange' in win ? 'onorientationchange' : 'resize';

    if(!metaEl){
        metaEl = document.createElement('meta');
        metaEl.setAttribute('name','viewport');
        metaEl.setAttribute('content','initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=0');
        html.firstElementChild.appendChild(metaEl);
    }

    function layoutCalc(){

        var width = html.getBoundingClientRect().width,
            ratio = width / baseWidth * 100,
            devicePixelRatio = window.devicePixelRatio,
            rem = ratio < 100 ?  ratio < 50 ? 50 : ratio : 100;

        if(!/\.\d+/.test(devicePixelRatio.toString())){
            html.dataset.dpr = devicePixelRatio;
        }

        html.style.fontSize = rem + 'px';

    }

    win.addEventListener(event,function(){
        clearTimeout(timer);
        timer = setTimeout(layoutCalc,300);
    },false);

    win.addEventListener('pageShow',function(e){
        if(e.persisted){
            clearTimeout(timer);
            timer = setTimeout(layoutCalc,300);
        }
    },false);

    layoutCalc();

}(window,document));

功能说明:

· 自定义基准页面尺寸
通过为 html 标签添加 data-basewidth 属性来自定义指定基准页面的尺寸。
示例:

<html data-basewidth="750" >
</html>

· 定义页面内容的字体大小
对于一些符合标准的dpr值(只要是整数,例如:1,2,3),都会为 html 标签再附加一个 data-dpr 属性,然后开发者便可以根据这个属性为条件,实现在不同dpr情况下,对内容字体的大小的调整。
示例代码:

html[data-dpr="1"] .dpr-text{
    font-size:12px;
}
html[data-dpr="2"] .dpr-text{
    font-size:24px;
}
html[data-dpr="3"] .dpr-text{
    font-size:36px;
}
<p class="dpr-text">测试文字</p>

相比“响应式方式”JS自动换算无需添加规则,适合于各类型的显示设备。

缩放方式

同样是“缩放” REM方式在缩放的时候,会改变元素的尺寸,而下面要提到的缩放方式,并不会改变元素的原有尺寸,可以理解成只是在视觉上将内容或者页面缩放至与当前显示设备相匹配的尺寸。
这种缩放方式,也可以分为两种类型:

· 通过CSS3的transform对元素进行缩放。
· 对viewport进行缩放。

CSS3 缩放

首先贴出具体的代码(我已经做好注释):

function pageScale(opt) {
    var ua = navigator.userAgent,
        wp = ua.match(/Windows Phone ([\d.]+)/),
        android = ua.match(/(Android);?[\s\/]+([\d.]+)?/),
        dom = document.querySelectorAll(opt.selector), // 获取要缩放的DOM,可以多个。
        dw = document.documentElement.clientWidth, // 获取当前显示设备的宽度
        dh = document.documentElement.clientHeight, // 获取当前显示设备的高度
        pw = opt.width || 320, //获取设计稿的最小尺寸 - 宽度
        ph = opt.height || 568, // 获取设计稿的最小尺寸 - 高度
        mode = opt.mode || 'cover', // 获取显示模式
        origin = opt.origin || 'left top 0', // 设置缩放中心点
        ratio = 0, // 定义比值
        i = dom.length; // 获取要调整DOM的个数

    // 根据模式的不同,从而生成响应的比值,总的来说contain模式只关注宽高中最小的那个。
    if (mode == "contain") {
        if (pw > ph) {
            ratio = dw / pw;
        } else {
            ratio = dh / ph;
        }

    } else if (mode == "cover") { // cover模式下,只关注宽高最大的那个。
        if (pw < ph) {
            ratio = dw / pw;
        } else {
            ratio = dh / ph;
        }

    } else {
        ratio = dw / pw;
    }

    function calcScale(_mode, obj, num) {

        var _o = obj.style;
        _o.width = pw + "px";
        _o.height = ph + "px";
        _o.webkitTransformOrigin = origin;
        _o.transformOrigin = origin;
        _o.webkitTransform = "scale(" + num + ")";
        _o.transform = "scale(" + num + ")";
        // 兼容android 2.3.5系统下body高度不自动刷新的bug
        if (_mode == "auto" && android) {
            document.body.style.height = ph * num + "px";
        } else
        if (_mode == "contain" || _mode == "cover") { //因为通过transform进行缩放,并不会导致DOM的重绘,因此就不会改变DOM的原有尺寸,所以在缩放的时候,还是根绝原有尺寸进行缩放,从而导致缩放后位置发生偏移。从而需要定位进行位置的调整。
            _o.position = "absolute";
            _o.left = (dw - pw) / 2 + "px";
            _o.top = (dh - ph) / 2 + "px";
            _o.webkitTransformOrigin = "center center 0";
            _o.transformOrigin = "center center 0";
            // 阻止默认滑屏事件
            if (wp) {
                document.body.style.msTouchAction = "none";
            } else {
                document.ontouchmove = function (e) {
                    e.preventDefault()
                }
            }
        }
    }

    // 循环DOM,依次执行缩放计算函数。
    while (--i >= 0) {
        calcScale(mode, dom[i], ratio);
    }

}

调用方式:

 window.onload = window.onresize = function(){
     pageScale({
         selector : '.box', //模块的类名
         mode : 'cover',    // auto || contain || cover 
         width : '320',     //默认宽320px 
         height : '568',     //默认高568px
         origin : 'center center 0' //缩放中心点,可选,在contain和cover模式下无效,默认为"left top 0"
     })
  }

这种CSS3缩放实现的移动端适配,是我在白树的一篇《pageResponse - 让H5适配移动设备全家》博客中看到的,然后我在其基础上按照自己的理解,进行了小小的修改。
总的来说,想使用 白树 这种缩放的方式,第一步需要准备一个大而且高清的设计稿,比如按照iphone5的 640px*1136px,然后调用方法时,要注意在参数中宽度与高度都要按照最小适配的那个尺寸为起始,例如我这里就是 320px*568px 。尤其要注意的是,如果页面中有插入图,需要将插入图的尺寸调整为 320*568。
具体HTML代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
    <title>pageResponse</title>
</head>
<body>
    <div class="box">
        <img src="demo.jpg" width="320" height="504" alt="">
    </div>
</body>
</html>

viewport 缩放

viewport缩放,它的大致原理是取当前viewport的deviceWidth值与设计稿的宽进行相除,然后得到两者相差的比例值,再设置meta标签的initial-scale的为这个比例值,从而缩放整个viewport,这种方式的优点在于布局时,完全可以按照PC端方式(可以用px为单位哦)进行布局,并且对于高版本的安卓,通过设置target-densitydpi属性 让其自动调整物理像素与逻辑像素的对应关系。
具体代码如下:

var phoneWidth = parseInt(window.screen.width); // 取得显示设备的逻辑像素,需要注意的是 screen.width方式 可能不准
var phoneScale = 680 / phoneWidth;

var ua = navigator.userAgent;
if (/Android (\d+\.\d+)/.test(ua)) {
    var version = parseFloat(RegExp.$1);
    // andriod 2.3到4.0
    if ((version > 2.3) && (version < 3.0)) {
        document.write('<meta name="viewport" content="width=680, minimum-scale = ' + phoneScale + ', maximum-scale = ' + phoneScale + ',user-scalable=no,target-densitydpi=device-dpi">');

    } else {
        // andriod 2.3一下那完全不需要 谁特么还用那么老的东西?!
        document.write('<meta name="viewport" content="width=680, user-scalable=no, target-densitydpi=device-dpi">');
    }
    // 其他系统
} else {
    document.write('<meta name="viewport" content="width=680, user-scalable=no, target-densitydpi=device-dpi">');
}

总的说,缩放方式实际上有一种缺陷,那就是ios系统会出现元素显示区域与实际元素所在区域有一定的偏移,影响文本、图片的查看和复制,微信中无法识别二维码。在没有文本复制和识别二维码的页面时可以用这种方法。


参考链接:
http://www.codeceo.com/article/font-size-web-design.html
https://github.com/amfe/lib-flexible
http://www.xiaoxiangzi.com/Programme/CSS/4298.html

posted @ 2019-03-21 21:26  啊睦  阅读(223)  评论(0编辑  收藏  举报