第一次写移动端自适应布局?那就对了

时间:2021-1-8 作者:admin

一、前言

随着移动互联网的发展,做出来的网页可能显示在不同屏幕大小的设备上,为了针对不同大小的屏幕都可以有良好的样式体验特别是一套代码适配多端的情况,自然有不同布局方案设计出来,我主要把他们分为三类

  • 基于宽度百分比和浮动配合的栅格布局或者叫流式布局,flex 弹性布局, grid 多维网格布局等。由于 flex 在这类布局中是主流,为了方便就统一称为弹性布局。

  • 基于 @media 的媒体查询,可以根据查询条件选择执行特定的样式代码,从而实现不同的布局,由于主要是针对设备特性进行条件查询,所以可以称为媒体布局。(媒体查询条件一览 这里提一点经常用的 device-aspect-ratio 这个查询设备像素比的条件已经被废弃了由 resolution 来取代)

  • 基于 rem 或者是 vm 等单位实现的一套布局,这类布局的特点是严格按照设计图的比例来呈现页面,可以称为等比例布局或者叫自适应布局。

关于布局的名称,其实是没有统一的约定,主要是方便下文的叙述,这三类布局在实际使用时也是可以混合使用的,主要由实际场景决定。弹性布局和媒体布局在了解了基本的 css 知识后都比较容易理解(很久前写的一个flex布局测试页面,方便学习用的),而对于自适应布局,可能有点不好理解,当然我会以最少的概念来说,一开始太多的抽象概念反而阻碍对实质的理解。

由上面的自适应布局的特点,即严格按照设计图比例来呈现网页,重点是按照比例,不是尺寸,这个很重要。那是什么比例呢,对于网页设计来说一般可以简单地概括为设计元素的尺寸比上设计图的宽度尺寸,这个有点像地图的比例尺,只要按照比例尺的比例画图就可以把真实的地形画在一张有限尺寸的纸上。对于我们网站开发来说,其实也是一样,我们只需要找来一张图纸,然后按照比例把图重新画在图纸上就是了,其他可以不用管,只要保证重新画上的元素的尺寸比上图纸宽度等于设计元素的尺寸比上设计图的宽度就可以了,而这里的图纸宽度在浏览器上就叫做布局宽度,可以通过 document.documentElement.clientWidth 来获取。到目前为止只需要记住这一个概念就可以了,下面我会以一个素人的角度来展开,通过素人的经历,相信你会对自适应布局有更好的理解

二、viewport 初识

小明是一个街口职业技术学院的毕业生,毕业后听说前端妹子漂亮,然后就去应聘了一个叫前端的职位,还应聘上了,但是上岗后发现前端没有妹子,迫于就业压力,也只好迎难而上了。很快小明就熟悉了公司的业务,而且了解到和他对接的 UI 是一个妹子,名字叫小红,他就变得更加好学了,心想一定能完美实现小红的设计效果。很快就有一项任务交给了小明,而且小红也给了设计图,其中 PM 啊强嘱咐小明要在不同大小的手机屏幕都要按照设计图比例来显示,小明表示这完全没有问题 。

小红的设计图

小明仔细研究了一番设计图,发现就只有两个方块和一条分割线,要比例一致立即就想到了宽高百分比,但很快就发现了问题,高度的百分比不是相对宽度的,而且要让高度百分比起作用,上面的 body html 都要设置高度,问题是设计图居然没有标记高度,看来这种方法不行。否定这个方法后,小明没有止步不前,立即就写下了下面的实现方案,说不定浏览器本身就自已自动适配呢

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>浏览器请自动适配</title>
  <style>
    body {
      margin: 0
    }
    .top {
      width: 750px;
      height: 100px;
      background-color: green;
      border-bottom: 1px solid black;
    }
    .left {
      width: 200px;
      height: 500px;
      background-color: red;
    }
  </style>
</head>
<body>
  <div class="top"></div>    
  <div class="left"></div>    
</body>
</html>

但很明显小明想多了,而且他很快发现了一个奇怪的问题,他打开 chrome 浏览器的控制台的移动端模式,把尺寸设置为 750px,还是这样,他想那肯定是 dpr 问题,如是他把 dpr 调节为 1 2 3 还是一样,需然这个时候还不知道什么是 dpr(在线查看该例子)

宽度 750px DPR 1 2 3 下的样子
小明被头顶那块绿震惊了,为什么缩水了?小明通过审查元素,发现其宽高还是设置的宽高,作为一个学过 js 的人,小明觉得不能被 css 所迷惑,然后在控制台输入了下面的代码

    document.querySelector('.top').style.width

很快浏览器就输出了 "",很明显宽度样式是设置在 css style 标签上的,不是设置元素的 style 内联属性上,自然会返回空字符串,小明很快就看穿了这个问题,接着写下了

  getComputedStyle(document.querySelector('.top')).width

浏览器返回了 "750px",看来控制台的 css 没有欺骗我啊,小明感慨道,但是小明还是不死心

  document.querySelector('.top').getBoundingClientRect().width

浏览器依然输出 750
这就奇怪了,调试的尺寸为 750px,元素的宽度也是 750px,怎么就不能占满呢?是不是浏览器有 bug
小明是个聪明人,他很快就打开搜索引擎输入了 第一次写移动端布局遇到了 bug 怎么办 不出意外他找到了这篇文章,并很快看到了前言,很明显了他记住了布局宽度和 document.documentElement.clientWidth,他也是一个务实不会摸鱼的人并没有浪费时间在继续阅读这篇文章上,他显然不知道这篇文章主角是他。

事情很快就给了小明一个答案

  document.documentElement.clientWidth

浏览器输出了 980,对就是 980,这个就是浏览器的默认布局宽度,我写的元素就是放在这个大小上的,小明胸有成竹地说道。小明也是一个富有想象力的人,他先把自已化身为一个浏览器,然后用浏览器的身份把自已化身为一个镜头,现在镜头出现了一个物体,物体的大小超过了镜头的视野,那么镜头应该怎么可以记录下整个物体呢?
其实有两个方法,第一个方法是让镜头移动或者是让物体移动,通过镜头和物体的相对移动就可以记录到物体整个大小。
第二个方法是,把镜头和物体的距离拉远,视野开阔了,自然就可以看到整个物体,相对来说物体就看似变小了。
这两种方法中,镜头和物体的大小其实一直都没有变,变的只是距离和相对移动的坐标。而这里的镜头就是浏览器的可视区域,很多时候就是屏幕区域,而物体显然就是布局区域,那么浏览器是采用何种方法来显示布局区域呢,如果是采用第一种方法,那么显然 980 的布局宽度是大于屏幕宽度的,如果要浏览整个网页,自然就需要向左滑动,类似于相对移动,很明显事实不是这样,整个页面都能看到,那肯定是浏览器通过拉大距离来把页面塞进去,得到的效果就像是把整个布局区域都缩小塞进了屏幕区域内,很显然这种缩放也是等比例缩放的。
小明毕竟是一个务实的人,他很快从工程部借来一把游标卡尺,对着屏幕量起了元素的尺寸来(px 和实际距离的换算是基于特定设备的,有兴趣可以看这里),并查询了设备的 dpi(每英寸有多少像素) 值,最后计算出顶部的绿色块的大小应该是 575px,这个大小就是缩放后元素的尺寸值,后面只需要计算对应比例是否一致就可以得出浏览器是否采用了自动缩放来实现这个效果

  575 / 750 = 0.7666666666666667
  750 / 980 = 0.7653061224489796

毕竟是使用游标卡尺测量的,这点误差其实是可以接受的,小明继续测量了其他的尺寸,也得到类似答案,最后小明总结了一条要使网页符合要求的公式,

这个是要求: 最后缩放后的实际大小 / 屏幕的宽度 = 设计图的尺寸 / 设计图的宽度
因为: 最后缩放后的实际大小 / 屏幕的宽度 = css 尺寸的大小 / 布局宽度
所以保证: css 尺寸的大小 / 布局宽度 = 设计图的尺寸 / 设计图的宽度
就可以实现需要的要求

设计图和设计图的宽度还有布局宽度都是已知的,然后通过换算就可以计算每一个元素的尺寸大小了,很快小明就写了下面的版本

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>浏览器布局980</title>
  <style>
    body {
      margin: 0;
      background-color: blue;
    }
    .top {
      width: 980px;
      height: 130.67px;
      background-color: green;
      border-bottom: 1.37px solid black;
    }
    .left {
      width: 261.33px;
      height: 653.33px;
      background-color: red;
    }
  </style>
</head>
<body>
  <div class="top"></div>    
  <div class="left"></div>    
</body>
</html>

小明打开了控制台并调节了不同的宽度和 dpr 效果都很好,当然从上面的公式来看其实和 dpr 也是无关的。(在线查看该例子)

宽度 474x DPR 3 下的样子

但是很快小明就找到问题,如果调节的宽度超过 980,布局宽度就不再是 980,而是变为调节的宽度,这个时候尺寸的比值就不再被维持,而当尺寸的宽度是少于 250 时,浏览器不再等比缩放了,而是采取上面的说到的第一种方法,这个时候上面公式也不再被维持。当然小明内心是毫无波澜的,毕竟谁的手机是这种尺寸的,如果是那就不是手机了

不过这明显是有问题的,那就是肯定不是全部的手机浏览器的默认布局是 980,如果遇到不是 980,那肯定就不行了,那有什么办法能够设置浏览器布局宽度呢?小明毕竟是一个聪明人,既然可以从 document.documentElement.clientWidth 从获取值,那应该也能设置值吧,

document.documentElement.clientWidth = 500
500
document.documentElement.clientWidth
980

很明显小明想太多了,不能使用这个方法来设置布局宽度。前面提到小明只看到前言,但是 viewport 初识 这个标题,他应该是看到的,很快他就找到了设置浏览器布局宽度的方法

  <meta name="viewport" content="width=">

那么 width 应该设置多少呢?小明翻了翻公式

css 尺寸的大小 / 布局宽度 = 设计图的尺寸 / 设计图的宽度

如果把布局宽度设置为设计图宽度,那 css 尺寸的大小不就可以设置为设计图的尺寸,而且还不用执行额外的计算,那实在是太好了,小明明显为了自已发现而兴奋不已,如是写下了下面的版本

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>浏览器布局980</title>
  <meta name="viewport" content="width=750">
  <style>
    body {
      margin: 0;
      background-color: blue;
    }
    .top {
      width: 750px;
      height: 100px;
      background-color: green;
      border-bottom: 1px solid black;
    }
    .left {
      width: 200px;
      height: 500px;
      background-color: red;
    }
  </style>
</head>
<body>
  <div class="top"></div>    
  <div class="left"></div>
</body>
</html>

同样在不同的宽度和不同的 dpr 有很好的效果(在线查看该例子)

宽度 520px DPR 2 下的样子

而且调节屏幕宽度大小,当超过布局宽度时,也不会改变变布局宽度,但是依然在少于 250 时遇到上面的情况,不过这显然也不会有影响,于是小明就愉快地提交了任务。

参考资料

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。