web移动端适配

879 阅读9分钟

适配达到的效果是什么?不同尺寸的手机设备上,页面“相对性的达到合理的展示(自适应)”或者“保持统一效果的等比缩放(看起来差不多)”。

基本概念

在了解适配方案前,让我们来了解一些基本的概念。

以iphone6为例

  • 分辨率:750pt×1334pt
  • 屏幕尺寸:4.7in
  • 屏幕像素密度:326ppi

物理像素(physical pixel)

物理像素又被称为设备像素,他是显示设备中一个最微小的物理部件。每个像素可以根据操作系统设置自己的颜色和亮度。所谓的一倍屏、二倍屏(Retina)、三倍屏,指的是设备以多少物理像素来显示一个CSS像素,也就是说,多倍屏以更多更精细的物理像素点来显示一个CSS像素点,在普通屏幕下1个CSS像素对应1个物理像素,而在Retina屏幕下,1个CSS像素对应的却是4个物理像素。

iphone6屏幕上垂直有1334个物理像素,水平有750个物理像素。

css像素(device-independent pixel)

设备独立像素,不同于设备像素(物理像素),它是虚拟化的。比如说css像素,我们常说的10px其实指的就是它。

设备像素比dpr(device pixel ratio)

设备像素比简称为dpr,其定义了物理像素和设备独立像素的对应关系

设备像素比 = 物理像素 / 设备独立像素

屏幕像素密度PPI(pixel per inch)

屏幕像素密度是指一个设备表面上存在的像素数量,它通常以每英寸有多少像素来计算(PPI)。屏幕像素密度与屏幕尺寸和屏幕分辨率有关,在单一变化条件下,屏幕尺寸越小、分辨率越高,像素密度越大,反之越小。

屏幕密度 = 对角线分辨率/屏幕尺寸

视口

布局视口(layout viewport)

简单来说就是DOM宽度

PC浏览器中,有一个用来约束CSS布局视口的东西,又叫做初始包含块。这也就是所有宽高继承的由来。除去margin、padding,布局视口和浏览器可视窗口宽度是一致的,同时也和浏览器本身的宽度一致。 但是在移动端不一样 继续以iphone6为例,这里没加meta标签

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    html,
    body {
      height: 100%;
      width: 100%;
    }

    .left {
      float: left;
      width: 20%;
      height: 100%;
      background: red;
    }

    .right {
      float: right;
      width: 80%;
      height: 100%;
      background: green;
    }
  </style>
</head>

<body>
  <div class="left"></div>
  <div class="right"></div>
</body>

</html>

我们会看到body的宽度是980px,而浏览器的宽度只有375px。这里的980px就是移动端所谓的布局视口了

视觉视口(visual viewport)

简而言之就是屏幕宽度

理想视口(ideal viewport)

布局视口很明显对用户十分的不友好,完全忽略了手机本来的尺寸。 所以苹果引入了理想视口的概念,它是对设备来说最理想的布局视口尺寸。理想视口中的网页用户最理想的宽度,用户进入页面的时候不需要缩放。 那么很明显,所谓的理想宽度就是浏览器(屏幕)的宽度了,使布局视口就是视觉视口

对pc端来说:

状态 布局视口 视觉视口
默认 相等 相等
放大 变小 变小
缩小 变大 变大

对移动端来说:

状态 布局视口 视觉视口
默认 偏大 相等
放大 不变 变小
缩小 变大 变大

meta标签做了什么

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

width=device-width 这句代码可以把布局视口设置成为浏览器(屏幕)的宽度

initial-scale=1 的意思是初始缩放的比例是1,使用它的时候,同时也会将布局视口的尺寸设置为缩放后的尺寸。而缩放的尺寸就是基于屏幕的宽度来的,也就起到了和width=device-width同样的效果

相关实现方式

动态设置视口缩放

主要三点:

  • 设置理想视口
  • 动态设置视口缩放为1/dpr
  • px单位的适配

设置理想视口

设置理想视口,使得DOM宽度(layout viewport)屏幕宽度(视觉视口)(visual viewport)一样大,DOM文档主宽度即为屏幕宽度。1个CSS像素(1px)由多少设备像素显示由具体设备而不同。

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

动态设置视口缩放为1/dpr

对于安卓,所有设备缩放设为1,对于IOS,根据dpr不同,设置其缩放为dpr倒数。设置页面缩放可以使得1个CSS像素(1px)由1个设备像素来显示,从而提高显示精度;因此,设置1/dpr的缩放视口,可以画出1px的边框。

不管页面中有没有设置viewport,若无,则设置,若有,则改写,设置其scale为1/dpr。

(function (doc, win) {
  var docEl = win.document.documentElement;
  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;

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

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

  docEl.setAttribute('data-dpr', dpr);
  // 动态改写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);
  } else {
    metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
  }

})(document, window);

px单位适配

类似于这样,在动态设置scale的时候,可以给元素加上[data-dpr="2"]这样的属性进行字体适配(但是无法适配1.75这样的...)

.p {
    font-size: 14px;

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

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

rem

定义:font size of the root element

而rem是相对于根元素,这样就意味着,我们只需要在根元素确定一个参考值,使得以rem为单位的元素在不同终端上以相对一致的视觉效果呈现。

flex布局

更多内容请参考阮一峰老师的:

vm/vh

视窗(Viewport)是你的浏览器实际显示内容的区域——换句话说是你的不包括工具栏和按钮的网页浏览器。这些单位是vw,vh,vmin和vmax。它们都代表了浏览器(视窗(Viewport))尺寸的比例和窗口大小调整产生的规模改变。

  • vw——代表视窗(Viewport)的宽度为1%。
  • vh——窗口高度的百分比。
  • vmin——vmin的值是当前vw和vh中较小的值。
  • vmax——大尺寸的百分比。

你可以在任何一个可以使用像素值的地方使用它们,比如width,height,margin,font-size等等。它们将通过窗口大小的调整或旋转设备的浏览器来重新计算这些值。

响应式布局

响应式针对的是不同分辨率设备而进行的适配式设计,以利用@media规则为主要手段,而自适应则忽略@media以比例布局为主,目的是适应不同的浏览器窗口大小。

响应式的问题在于:

  • 屏幕分辨率分区间:区间内无法进行区分,无法实现100%兼容,一般是用主流分辨率来进行划分;
  • 额外的工作量:响应式布局的工作都是需要开发者去实现的,带来了额外的开放量; 不适合功能复杂的页面:响应式一般适合用于资讯类页面,功能复杂的网站对于页面的整体排版和样式要求较高(特别是对比PC和H5)
  • 影响性能(试想一下淘宝这种大型网站,一个分页下的商品条目特别多,并且每个商品条目的dom结构又十分复杂,而且pc端往往显示的信息是要比手机端更多的。如果不分开做两套,而是直接用响应式的话,那么pc端上显示的很多dom就要在手机端上隐藏,结果这些dom都没有被用到,但是却加载了。在这个流量和速度至上的时代,代码冗余先不说,多加载的这些无用的代码而消耗的流量,从某种意义上来说就已经损失了很多的效益)

移动端适配方案

固定高度,宽度自适应

属于自适应布局,viewport width 设置为 device-width,以较小宽度(如 320px)的视觉稿作为参照进行布局。垂直方向的高度和间距使用定值,水平方向混合使用定值和百分比或者利用弹性布局

关键点:

  • 以小宽度作为参照是因为如果布局满足了小宽度的摆放,当屏幕变宽时,简单的填充空白就可以了;而如果反过来就可能造成“挤坏了”。
  • 需要小宽度的布局,又需要大宽度的图像,这是一个矛盾点。 320px 过于窄小,不利于页面的设计;只能设计横向拉伸的元素布局,存在很多局限性。

固定宽度,viewport 缩放

视觉稿、页面宽度、viewport width 使用统一宽度,利用浏览器自身缩放完成适配。页面样式(包括图像元素)完全按照视觉稿的尺寸,使用定值单位 (px、em)即可完成。

利用 rem 布局

依照某特定宽度设定 rem 值(即 html 的 font-size),页面任何需要弹性适配的元素,尺寸均换算为 rem 进行布局;当页面渲染时,根据页面有效宽度进行计算,调整 rem 的大小,动态缩放以达到适配的效果。利用该方案,还可以根据 devicePixelRatio 设定 initial-scale 来放大 viewport,使页面按照物理像素渲染,提升清晰度。

具体参考:使用Flexible实现手淘H5页面的终端适配

参考资料