移动端适配方案

在 PC 端,除了数据可视化和中后台这样一些全屏设计场景需要适配,绝大多数的网页采用的都是固定尺寸设计,由于 1366+ 的屏幕尺寸大于常用的固定设计尺寸,网页按设计尺寸 1:1 还原即可,不需要考虑适配。而在移动端,由于屏幕尺寸大小不一,且采用全屏的设计风格,则需要做适配。

目前移动端端内主流适配方案有:

1
2
3
* 流体式/弹性式;
* Layout Viewport Scale;
* VW + Rem;

注:适配方案还有设备独立像素和 Media Query(query 的是逻辑像素)。

抽象的设备独立像素解决的是端内高密度屏的适配(底层图形系统会根据 DPI 自动换算为物理像素),Media Query 解决的是跨端的设计风格适配,而流体、Rem、Layout Viewport Scale 解决的是端内设计要素(盒模型、字体、图片…)的适配(同一 UI 的整体缩放,没有设计上的差异)。

在设计要素适配中,“盒模型”和“字体”只需要考虑大小,而“图像”相对复杂一些,需要考虑流量、清晰度等问题,常见的解决方案有:矢量化、字体化、image-set 等。

流体式

这是最早使用的适配方案,也是最常用的一种,特别是在响应式 UI 框架中(比如 Bootstrap)。其原理是,完美视口下,选需要兼容设备的最小宽度(一般都是 320px)来布局,垂直方向的高度和间距使用定值,水平方向用百分比、定值、flex…,最终达到“当手机屏幕变化时,横向拉伸或者填充空白的效果”。Eg:百度亚马逊

使用方法

  • 步骤一,设置 Layout Viewport 为完美视口
1
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  • 步骤二,切图布局

小往大适配,选需要兼容设备的最小尺寸还原设计稿(一般都是 iPhone5 的 320px,这是当前主流手机中的最小尺寸),当屏幕变宽时,简单的填充空白即可(如果从大到小,小分辨率设备上,就会出现滚动条)。

注:选主流中最小分辨率机型来进行设计(640 * 1334),CSS 尺寸 = 视觉稿尺寸/dpr = 640/2 = 320。前端在高清视觉稿下切图,使得图片兼容 Retain,在 CSS 中缩放;

1
2
.img {max-width: 100%;}
.icon {background: url(....) no-repeat 0 0/cover;}

优缺点

  • 优点
1
2
* 简单方便;
* 兼容第三方组件(echarts、mintUI...);
  • 缺点
1
* 还原度不精确(如果以开发规范驱动设计就不会存在这个问题) --- 在分辨率不同的设备上,页面的字体大小,内容尺寸都是一样的,不同的是,大屏的内容间的空隙比小屏的大;

Layout Viewport Scale

设计稿、页面布局、Layout Viewport 使用统一宽度,前端按设计稿的尺寸还原,使用定值单位(px、em…),利用浏览器『自身缩放』完成适配。Eg:网易新闻

使用方法

  • 步骤一,设置 Layout Viewport 的宽度

一般会设为 640。与流体式一样,这两种方案的核心都是视口的确定。

1
<meta name="viewport" content="width=设计稿的宽度">

该方案使用固定宽度值,需要考虑主流分辨率,确定一个合适的值,既不能选大了,也不能选小了,选大了可能会使得在小分辨率屏幕下像素丢失,选小了又可能会使得在大分辨率屏幕下模糊。

  • 步骤二,缩放

正常浏览器都会将 Layout Viewport 自动缩放至屏内(视觉视口),不能自动缩放的浏览器要手动计算 scale 值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var viewport = function () {
var metaEle = document.querySelector('meta[name="viewport"]'),
metaCon = metaEle ? metaEle.content : '',
matchScale = metaCon.match(/initial\-scale=([\d\.]+)/),
matchWidth = metaCon.match(/width=([^,\s]+)/);

if (metaEle && !matchScale && (matchWidth && matchWidth[1] != 'device-width')) {
var layoutViewportWidth = parseInt(matchWidth[1]),
screenWidth = screen.width;

scale = screenWidth / layoutViewportWidth;

if (scale < 1) {
metaEle.content += ',initial-scale=' + scale + ',maximum-scale=' + scale + ', minimum-scale=' + scale + ',user-scalable=no';
}
}
};

viewport();

以下面动态生成的结果为例,640 是根据设计稿定下的,0.5(1/dpr,或者 320/640) 是根据屏幕宽度动态生成的。生成的 viewport 告诉浏览器网页的布局视口使用 640px,然后把页面缩放成 50%,图片、文字等等所有元素都被缩放在手机屏幕中,这是绝对的等比例缩放。

1
<meta name="viewport" content="width=640,initial-scale=0.5,maximum-scale=0.5,minimum-scale=0.5,user-scalable=no">

优缺点

  • 优点
1
2
* 开发简单 --- 缩放交给浏览器,完全按视觉稿切图;
* 还原精准 --- 绝对等比例缩放,可以精准还原视觉稿;
  • 缺点
1
2
* 像素丢失 --- 对于一些分辨率较低的手机,可能设备像素还未达到指定的 viewport 宽度,此时屏幕的渲染可能就不准确。比较常见的是边框“消失”了,不过随着手机硬件的更新,这个问题会越来越少;
* 不兼容第三方组件和富文本数据 --- 这也是最大的问题;

REM

“流体式”、“Layout Viewport Scale” 这两种适配方案,都存在缺陷,一个优秀的适配方案应做到以下两点:

1
2
* UI 的精确还原与适配
* 兼容第三方组件和富文本数据

VW + Rem 就是这样的一个方案。其原理是,屏幕联动 html font-size vw,html font-size 联动 rem( html font-size value = 1 rem),来做到适配。

使用方法

  • 定义规范
1
2
* 设计规范。规定设计稿分辨率,推荐采用 1X 的设计方案(即手机 375,横屏 Pad 960,而不是 2X 的 750、1920),为了方便 figma 下,导入第三方 UI 组件
* 基准值。规定屏幕分成 10 等分(100vw/10),html font-size 为 10vw,所以 1rem = 10vm

将基准值的定义为 100vw/10 = 10vw,而不是 100px(除以 19.2、12.8),这仅仅是规范,10vw 体现适配原则,容易理解,而 100px 在没有转换工具的条件下,方便了计算。

在没有转换工具的情况下,为了方便计算,我们会将 html faont-size 定义如下:

1
2
3
4
5
/* 1920 的设计规范下还原的,base 为 1920px/19.2 = 100px,px 转 rem 除以 100 即可 */
/* calc(100vw/19.2) 不直接写成 10vw,是为了体现 1920 的设计规范 */
html {
font-size: calc(100vw/19.2);
}

直接在源码里面进行转换不利于源码的维护,在现代前端开发中推荐使用工程化工具转换。

  • 使用步骤
1
2
3
* 步骤一:定义 html font-size 基准值(一般会定为 10vw,绝大多数适配场景都是按宽度适配,如果按高度适配也可为 10vh)
* 步骤二:开发页面。页面在设计尺寸下开发(比如,平板 1920、手机 750),开发单位为 px
* 步骤三:px 转 rem。这一步请用工具完成,转换算法为 px/基准值(1920 设计规范下是 192,750 设计稿是 75),一般会忽略 border,因为可能变成 0px
  • 兼容处理

vw 不支持 Android 4.4 以下设备,这时候就需要对 vw 做兼容处理了。这里不想通过 JS 来解决此问题,本着逐渐增强,平稳退化的原则,选一个默认分辨率处理,比如手机就选 750 的分辨率:

1
2
3
4
html {
font-size: 37.5px;
font-size: 10vw;
}

注:flexible 就是手淘团队在 Android 4.4- 设备不支持 vw 时的一个 JS 解决方案。