移动端适配:彻底搞懂企业级移动端适配方案

2,861 阅读14分钟

我不知道大家平时有没有这样以下两个疑问

一. 从像素开始搞懂移动端适配

1.经常听到手机发布会说的iPhone X参数,5.8寸屏幕,屏幕分辨率:2436x1125像素,458 ppi,我们在Chrome手机开发者模式的时候iPhone X分辨率又是375 x 812,大家是不是都傻傻分不清楚这些都是些啥概念。

2.为什么同样的图像能够在不同屏幕(1080p,2k,4k)展现出现不大的差异,分辨率高的屏幕看起来同样的图像为什么很清晰很清晰,甚至外接4k显示器的屏幕,写代码要放大字体2倍来看?

只有解决以上两个疑问,我们才真真理解屏幕的差异化,才能做好移动端适配

1. 物理像素(设备像素pt)

物理像素也叫设备像素,是屏幕制造出来就已经固定的,单位是pt。每个屏幕都是由很多发光的二极管组成,通过不同颜色发光的二极管排列展示成不同的图像,如下图所示。通过调整发光二极管你可以改变屏幕图像的颜色和亮度。每一个发光的二极管都被当作为一个物理像素点,上面疑问1iPhone X发布会说的2436x1125,就是硬件物理像素。

image.png

2.逻辑像素(CSS像素px)

  • 由于厂商不同,屏幕大小不同,我们不能直接用物理像素来做图像显示,这样例如400的物理像素宽的设备和2000的物理像素设备相比,同时看400pt腾讯视频画面那不是差距很大很大,一个只占到1/5内容不到,一个刚合适。但是现实中你发现差异并不大,只是整体变小了而已。为了兼容这些差异,提出了逻辑像素的概念.
  • 具体怎么实现了的呢,首先是在小屏幕400pt的设备上,使用1px=1pt,然后在大屏幕1px=5pt的形式,那这样400px的画面岂不是很完美的展示了,上面疑问1说的375 x 812就是逻辑像素,用于磨平设备之间差异所规定的,所以chrome上你调整很多手机的时候,他差异很小的原因都是3,400左右,这就是近似的磨平了物理像素差距过大

image.png

3.设备像素比(DPR)

设备像素比(DevicePixelRatio):DPR = 设备像素 / CSS 像素 例如,iPhone 6 物理像素为 750 x 1334,理想视口 375 x 667 ,DPR = 2 看了物理像素和逻辑像素的解释后,有些人会说手机物理分辨率甚至比电脑还要高,根本不是上面说的那样。始终要理解到物理像素跟设备大小不是直接关系,手机可能物理像素就是3000宽,但是很多大电脑才2000个物理像素宽,所以电脑上1px=1pt,手机上这么多分辨率不可能一样展示就展示的很小很小的内容了,就使用1px=4pt,逻辑像素比4,通过这个解释主要是让大家走出一个误区,屏幕大小跟分辨率没直接关系

设备像素比越高,就是DPR越高的屏幕,他展示那种设计师给的二倍图,三倍图拥有更多像素的图片吗,高清屏幕展现得更加细腻,屏幕显示差距就出来了,苹果的retina屏幕比高清屏多很多像素,所以都是2,3个像素显示1px,也就是平时开发过程中H5只能操作px,不能像安卓开发操作pt的原因。

代数设备操作系统逻辑分辨率(point)物理分辨率(pixel)屏幕尺寸(对角线长度)缩放因子
iPhone
第十一代iPhone 8iOS 11375 x 6671334 x 7504.7寸2x
第十一代iPhone 8 PlusiOS 11414 x 7362208 x 1242 (1920x1080)5.5寸3x
第十一代iPhone XiOS 11375 x 8122436 × 11255.8寸3x

4.PPI

PPI即每英寸所拥有的像素数目。pixels per inch所表示的是每英寸对角线上所拥有的像素(pixel)数目。

那么如何计算呢,首先知道疑问1中的发布会设备多少英寸,你买手机的时候发现iPhone X 5.5英寸拿到手咋个比iphone8 plus5.5英寸小这么多。因为他的屏幕尺寸指的屏幕的对角线长度,iPhone8-iPhoneX边框等等全变窄了,所以对于这种不要觉得疑惑。

2是算对角线的物理像素

PPI=√(X^2+Y^2)/ Z (X:长度像素数;Y:宽度像素数;Z:屏幕大小)。

iphonex ppi=√(2436^2+1125^2)/ 5.8=460左右的ppi

image.png

5.UI设计像素

平时我们使用psd等等设计图上面展示的像素是设计师根据屏幕不同做的设计图,比如移动端使用375的宽标准,PC端使用1920的宽,这样标准的设计图能更好的兼容设备,所以我们会根据UI设计图来做标准适配

6. 相对像素 em 和 rem

对于px是逻辑像素,他是绝对像素单位,不能根据设备来展示变化,虽然逻辑像素大部分被兼容到300-400,比如ipad是超过400的,电脑中1px=1pt,也就是1920等,差距就更大了,淘宝就是根据不同分辨率做了不同的设计图,不同放大缩小来处理。 对于想一套代码处理多种设备的,相对像素诞生了

  1. 相对像素 em。em 这个单位是相对于父元素的 font-size 字号值,实际就是父元素 font-size 的几倍。这个字号可以是 CSS 指定的,也可以是从父元素的父元素继承来的,如果前两个都没有,就会使用默认值(一般是16px)。假如父元素指定了 “font-size:20px;” ,子元素使用了 “width: 10em” ,那么子元素的实际宽度就是 200px。使用em单位注意html的继承关系,因为他是相对于父集来做的,比较容易错乱

  2. 相对像素 rem。rem相对于em的不同是,它是相对于根节点,也就是 HTML 节点的 font-size 属性指定的 px 值。页面里所有用 rem 作为长度单位的元素,就都以 HTML 为准,这样的好处是不会由于层级而导致错乱 把像素搞清楚了,后面我会循序渐渐地更新具体的屏幕适配方案

二. 多种方案一次性搞定移动端适配

理解viewport

移动端和PC端不同,PC端的由于大部分是1px=1pt,也就是一般PC逻辑分辨率超过很大,对应到展示在375左右的移动端设备上,内容肯定一屏幕展示不完的,这个时候肯定要有横向滚动条来展示全部内容,移动端就提出了两个窗口概念

视口当前可见的部分叫做可视视口(visual viewport) 。可视视口可能会比布局视口layout viewport )更小,他们的关系如下

    可视视口(屏幕展示的区域)+横向滚动条的区域=布局视口

viewport指的就是展示网页的区域,一般浏览器会将viewport设置980大于可视区域,这样就出现横向滚动条了,如下就是一般浏览器的默认viewport宽度

image.png 为了印证这个猜想,平时创建html文件的时候,你很可能都没去管meta的viewport标签,快捷键创建html,他会自动带上,然后打开chrome调试模式,如下

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>带有viewport</title>
    <style>
        html,body{
            background:red;
        }
    </style>
</head>
<body>
    
</body>
</html>

效果如下

image.png 如果去掉<meta name="viewport" content="width=device-width, initial-scale=1.0">,设置了viewport为视口宽度为设备宽度,所以可视窗口=布局窗口。取消了这个设置,就会展示默认的viewport宽度

image.png 关于viewport的具体参数,MDN上面很少介绍,参考三方网站详细表

width设置layout viewport  的宽度,为一个正整数,或字符串"width-device"
initial-scale设置页面的初始缩放值,为一个数字,可以带小数
minimum-scale允许用户的最小缩放值,为一个数字,可以带小数
maximum-scale允许用户的最大缩放值,为一个数字,可以带小数
height设置layout viewport  的高度,这个属性对我们并不重要,很少使用
user-scalable是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes代表允许

其实主要平时用到的属性就如vscode插件自动生成的两句,仔细想为什么要写这两句,width=device-width设置视口宽度=布局宽度,但是在iphone和ipad上,无论是竖屏还是横屏,宽度都是竖屏一样的宽度。使用initial-scale=1.0是什么意思呢,就是将网页缩放到和视口宽度的100%,但这次轮到了windows phone 上的IE 无论是竖屏还是横屏都把宽度设为竖屏时的宽度。这个时候写两句的话,他就会选择最大的起效,兼容了所有设备

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

1.纯缩放百分比适配

假如一行四个div元素

div{
    width:25%
}

这种方式在bootstrap等响应式框架用的多,这样的优点有

  • 根据大屏幕,小屏幕都可以直接百分比,不会出现很大留白,显得页面极其不平衡
  • 不用关心px等宽度具体是多少,会自动适配
  • 不存在兼容性问题

缺点有

  • 不同屏幕宽度差距过大会造成页面按钮内容变形。
  • 完全相对于父元素,元素脱离文档流就会造成页面混乱

2.多套代码分端适配

这种采用媒介的形式,css提供了@media用于书写分段样式

     @media only screen and (min-width: 300px) {
            .main {
                width: 60px;
            }
        }

        @media only screen and (min-width: 400px) {
            .logo {
                width: 80px;
            }
        }

优点:

  • 显示更加灵活,比如一行在300px下面展示3个按钮,在400px就可以手动改为4个按钮 缺点
  • 需要维护多套设计图和多套CSS样式,维护难度大

3.flex和grid弹性布局适配

弹性布局是根设备宽度自适应撑满屏幕,根据屏幕自适应展示几个按钮,这种模式可以小范围适配,对于移动端H5适配还是要配合rem模式,并且兼容性不是很好。

4.rem适配

使用rem相对单位,例如根元素设置20px,那么1rem=20px,后续所有单位都根据根元素做换算,30px就是1.5rem,这种方式的优点如下

  • 可以通过改变根元素font-size按比例适配一个页面
  • 灵活根据尺寸自定义根节点的rem值大小 当然有缺点就是
  • 还是需要配合@media进行设置不同尺寸font-size的rem大小,不过rem配合@media已经是比较主流的方案了

5.使用rem+vw适配

为了做好浏览器的适配方案,参考网上的设计总结了一套方案

首先移动端的设计图都是相对于width:750px的标准设计图适配,第一步我们设定

100vw=750px
那么
1px=0.1333vw

然后第二步设定

设定    
    1rem=100px
    7.5rem=750px
那么
    1rem=100px=13.33vw

第三步采用@media做简单的区间适配

html {
    font-size: 13.33333vw;
}

@media screen and (max-width: 300px) {
  html {
  font-size:1rem;
    font-size: 13.33333vw;
  }
}

@media screen and (min-width: 300px) and (max-width: 400px) {
  html {
    font-size:1rem;
    font-size: 13.33333vw;
  }
}
...根据适配屏幕多少操作

然后由于真实计算出每个屏幕的rem对应的px是多少

html {
    font-size: 13.33333vw;
}

@media screen and (max-width: 300px) {
  html {
    font-size: 40px;//根据真实屏幕逻辑像素所计算出来的
    font-size: 13.33333vw;
  }
}

@media screen and (min-width: 300px) and (max-width: 400px) {
  html {
    font-size: 53.32px;//根据真实屏幕逻辑像素所计算出来的
    font-size: 13.33333vw;
  }
}
    

注意vw只能在安卓4.0和ios8以上使用

三. 企业级移动端适配3种方案实操

1. 开端

对于前两节像素,和实现方案的理解,我们总结了px+rem,px+vw,rem+vw三种模式的互相转换的方案,这一节我准备结合这三种如何转换和项目实操进行结合。

首先我们要明白一点,设计图始终是有一个以固定px为设计图(750xauto),或者有些设计师是以(375xauto)这种直接二倍图就可以了。然后将px转换为rem适配或者vw适配。

2. px+vw

这是网上推荐的比较多的方式

1.创建vue项目

npm i @vue/cli -g
vue create px-vw

2.安装 postcss-px-to-viewport 这个插件很方便的将px转换成vw

    npm i postcss-px-to-viewport --save-dev

3.配置postcss-px-to-viewport插件进行配置文件.postcssrc.js

module.exports = {
  plugins: {
    autoprefixer: {}, // 用来给不同的浏览器自动添加相应前缀,如-webkit-,-moz-等等
    "postcss-px-to-viewport": {
      unitToConvert: "px", // 要转化的单位
      viewportWidth: 750, // UI设计稿的宽度
      unitPrecision: 4, // 转换后的精度,即小数点位数
      propList: ["*"], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
      viewportUnit: "vw", // 指定需要转换成的视窗单位,默认vw
      fontViewportUnit: "vw", // 指定字体需要转换成的视窗单位,默认vw
      selectorBlackList: [""], // 指定不转换为视窗单位的类名,
      minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
      mediaQuery: true, // 是否在媒体查询的css代码中也进行转换,默认false
      replace: true, // 是否转换后直接更换属性值
      exclude: [/node_modules/], // 设置忽略文件,用正则做目录名匹配
      landscape: false, // 是否处理横屏情况
    },
  },
};

4.在app.vue书写简单适配环境

<template>
  <div class="app">
    <div class="header"></div>
    <div class="content"></div>
    <div class="footer"></div>
  </div>
</template>

<script>
export default {};
</script>

<style>
* {
  margin: 0;
  padding: 0;
}
.app {
  width: 750px;
  height: 1000px;
  background: red;
}
.header {
  width: 400px;
  height: 200px;
  background: black;
}

.content {
  width: 300px;
  height: 200px;
  background: yellow;
}

.footer {
  width: 200px;
  height: 200px;
  background: gray;
}
</style>

375 x auto image.png 540 x auto image.png

5.总结 这种模式实现起来非常简单

3. px+rem

对于rem我们知道是相对于根节点的多少倍数,那么我们只需要在根节点设置font-size为真实设备宽度的逻辑像素px,然后所有内容相对于根节点写多少倍rem即可

1.根据屏幕换算根节点的font-size

定义375 x auto的屏幕为font-size:14px

那么

width为屏幕宽度 fontSize = 14 * (width / 375) + "px";

2.根据屏幕变化更改响应式根节点计算

// 基准大小
const baseSize = 28;
// 设置 rem 函数
function setRem() {
  // 当前页面宽度相对于 750 宽的缩放比例,可根据自己需要修改。
  const proportion = document.documentElement.clientWidth / 750;
  // 设置页面根节点字体大小
  document.documentElement.style.fontSize = baseSize * proportion + "px";
}
// 初始化
setRem();
// 改变窗口大小时重新设置 rem
window.onresize = function () {
  setRem();
};

3.px-rem

经过步骤2的操作,我们设置了准确的根节点font-size,那么接下来进行的操作

image.png

image.png

经过步骤3的操作,我们自动设置了根节点的值,然后页面可以使用rem来书写代码,但是设计图都是750px的格式,里面所有量出来的尺寸都需要自己计算后写成rem,这显然是不行的,那么我们还需要将28px按比例转成1rem,我们设置的基准是750尺寸,28px(也就是真实逻辑分辨率375,14px)

4.安装postcss-pxtorem

    npm install postcss-pxtorem -D

5.配置.postcssrc.js

具体参数意思可以参考上面说到的px+pw适配方式里面有讲解

module.exports = {
  plugins: {
    autoprefixer: {}, // 用来给不同的浏览器自动添加相应前缀,如-webkit-,-moz-等等

    "postcss-pxtorem": {
      rootValue: 28,
      propList: ["*"],
    },
  },
};

总结:这时候已经转换成功了,px按比例转成rem,rem根节点安设配逻辑像素转成px

4.rem+vw

这种模式主要是解决兼容性问题,方案一直接转换成vw可能导致兼容性问题,这个时候我们需要做一个补丁,相当于设置了vw之前也加一层font-size具体px的设置,具体如何计算参考我的上一篇文章所提到的。

html {
    font-size: 13.33333vw;
}

@media screen and (max-width: 300px) {
  html {
    font-size: 40px;//根据真实屏幕逻辑像素所计算出来的
    font-size: 13.33333vw;
  }
}

@media screen and (min-width: 300px) and (max-width: 400px) {
  html {
    font-size: 53.32px;//根据真实屏幕逻辑像素所计算出来的
    font-size: 13.33333vw;
  }
}
   

这样根rem计算成功了,然后就是方案二一致的px转成rem就行了,这种方案我这边认为是比较完美的。