移动端前端适配方案总结

相关概念

在介绍各方案之前,先有必要了解一些必备的名词。

px

全称 pixel,像素。一个像素就是计算机屏幕所能显示一种特定颜色的最小区域。屏幕上显示数据最基本的点,不是长度单位。 如果点很小,那画面就清晰,我们称它为“分辨率高”,反之,就是“分辨率低”。

ppi

全称 Pixels Per Inch,屏幕像素密度。单位是 dpi,表示的是每英寸所拥有的像素(Pixel)数目。 越大屏幕越高清。

分辨率

屏幕分辨率确定计算机屏幕上显示多少信息的设置,以水平和垂直像素来衡量,iphone5 屏幕上垂直有 1136 个物理像素,水平有 640 个物理像素。查询设备的分辨率及 ppi

rem

相对长度单位。相对于根元素(即 html 元素)的 font-size 计算值的倍数。

物理像素(设备像素)

设备屏幕上的实际像素。如 iphone6 宽为 750

设备独立像素(逻辑像素/css 像素)

设备独立像素(也叫密度无关像素),可以认为是计算机坐标系统中得一个点,这个点代表一个可以由程序使用的虚拟像素(比如: css 像素),然后由相关系统转换为物理像素。如 iphone6375×667

设备像素比(device pixel ratio/屏幕分辩比/dpr)

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

在某一方向上,x 方向或者 y 方向。在控制台通过 window.devicePixelRatio 可得。

scale

scale 是屏幕拉伸比。也就是视口上的 initial-scale , maximum-sacle 等属性。

视口(viewport)

  • 布局视口:网页在开始设计时候的 dom 宽度(比如 960px

  • 可视视口:整个屏幕的视口(比如 iphone6 375px

  • 完美视口:

    1
    2
    3
    4
    5
    6
    7
    8
    <meta name="viewport" content="initial-scale=1.0,width=device-width,user-scalable=0,maximum-scale=1.0" />
    <!--
    width:设置布局视口的宽
    init-scale:设置页面的初始缩放程度
    minimum-scale:设置了页面最小缩放程度
    maximum-scale:设置了页面最大缩放程度
    user-scalable:是否允许用户对页面进行缩放操作
    -->

目前主流的自适应布局解决方案

响应式(Responsive web design)

通过媒体查询根据不同的屏幕分辨率来进行适配。

优点:

  • media query 可以做到设备像素比的判断,方法简单,成本低,特别是对移动和 PC 维护同一套代码的时候。目前像 Bootstrap 等框架使用这种方式布局。
  • 图片便于修改,只需修改 css 文件。
  • 调整屏幕宽度的时候不用刷新页面即可响应式展示。

缺点:

  • 代码量比较大,维护不方便。
  • 为了兼顾大屏幕或高清设备,会造成其他设备资源浪费,特别是加载图片资源。
  • 为了兼顾移动端和 PC 端各自响应式的展示效果,难免会损失各自特有的交互方式。

流式布局(推荐)

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

流式布局需要用到百分比或者 flex,即宽度永远铺满页面宽度,但高度和其他单位仍然用 px。它的字体精度可以保持跟设备系统一致(dpi)。

rem 布局

法一(推荐)

css:

1
2
3
4
5
html {
font-size: calc(100vw / 7.5);
} /* 以iphone6 750的设计稿为准 */

/* 其他元素用rem作为单位 */

js:

1
2
3
4
5
6
7
8
9
10
(function () {
function changeRootFont() {
var designWidth = 750,
rem2px = 100; //这个100理论上可以是任何值
document.documentElement.style.fontsize = (window.innerWidth / designWidth) * rem2px + 'px'; //iphone6: (375 / 750) * 100 + 'px';
// 因为chrome下最小字体大小为12px,所以不能把html的font-size设置成0.5px或者5px,50px是我们最好的选择。
}
changeRootFont();
window.addEventListener('resize', changeRootFont, false);
})();

此方法简单粗暴,无需考虑 dpr,物理像素之类。但缺陷就是 1px,图片高清,文字字体大小问题。文字大小可通过媒体查询来处理。计算量大,可通过 px2rem 插件来处理。

法二(不推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var dpr, rem, scale;
var docEl = document.documentElement;
var fontEl = document.createElement('style');
var metaEl = document.querySelector('meta[name="viewport"]');

dpr = window.devicePixelRatio || 1;
rem = (docEl.clientWidth * dpr) / 10;
scale = 1 / dpr;

// 设置viewport,进行缩放,达到高清效果
metaEl.setAttribute('content', 'width=' + dpr * docEl.clientWidth + ',initial-scale=' + scale + ',maximum-scale=' + scale + ', minimum-scale=' + scale + ',user-scalable=no');

// 设置data-dpr属性,留作的css hack之用
docEl.setAttribute('data-dpr', dpr);

// 动态写入样式
docEl.firstElementChild.appendChild(fontEl);
fontEl.innerHTML = 'html{font-size:' + rem + 'px!important;}';

// 给js调用的,某一dpr下rem和px之间的转换函数
window.rem2px = function (v) {
v = parseFloat(v);
return v * rem;
};
window.px2rem = function (v) {
v = parseFloat(v);
return v / rem;
};

window.dpr = dpr;
window.rem = rem;

引入以上代码即可用。通过 hackdata-dpr 来单独控制字体。

1
2
3
4
font-size: 16px;
[data-dpr='2'] input {
font-size: 32px;
}

此方案原理是动态修改 scalefont-size 来达到高清效果。类似的可参照阿里的 flexible

此方法解决了方法一存在的问题,缺陷是计算量大,可通过 px2rem 插件来处理。

scale 伸缩布局

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

法一

通过 js 更改 viewportinitial-scale

法二

写死 viewport 的宽度.

1
<meta name="viewport" content="width=360, user-scalable=no" />

优点:

  • 开发简单:缩放交给浏览器,完全按视觉稿切图。
  • 还原精准:绝对等比例缩放,可以精准还原视觉稿(不考虑清晰度的情况下)。
  • 测试方便:在 PC 端即可完成大部分测试,手机端只需酌情调整一些细节(比如图标、字体混合排列时,因为字体不同造成的对齐问题)。

缺点:

  • 像素丢失:对于一些分辨率较低的手机,可能设备像素还未达到指定的 viewport 宽度,此时屏幕的渲染可能就不准确了。比较常见的是边框“消失”了,不过随着手机硬件的更新,这个问题会越来越少的。
  • 缩放失效:某些安卓机不能正常的根据 meta 标签中 width 的值来缩放 viewport,需要配合 initial-scale
  • 文本折行:存在于缩放失效的机型中,某些手机为了便于文本的阅读,在文本到达 viewport 边缘(非元素容器的边缘)时即进行折行,而当 viewport 宽度被修正后,浏览器并没有正确的重绘,所以就发现文本没有占满整行。一些常用的段落性文本标签会存在该问题。
  • 不能开启 gpu raster 硬件加速:不能显式设置 minimum-scale=1.0,否则就达不到效果。而这个值是 chromium37 以上的 webview 触发 gpu raster 的一个条件,所以用这种方法就没法利用 gpu raster 硬件加速。

注:

其他问题

retina 下图片高清方案

  1. 两倍图片(@2x),然后图片容器缩小 50%(方便但会造成资源浪费)。
  2. 不同的 dpr 下,加载不同的尺寸的图片。

retina 下 1px 解决方案

元素 scale(方便但圆角等无法用)

1
2
3
4
5
6
7
8
9
10
11
12
13
.scale {
position: relative;
}
.scale:after {
content: '';
position: absolute;
bottom: 0px;
left: 0px;
right: 0px;
border-bottom: 1px solid #ddd;
-webkit-transform: scaleY(0.5);
-webkit-transform-origin: 0 0;
}

页面 scale(通用但页面整体都被缩放)

1
<meta name="viewport" content="width=width=device-width,initial-scale=0.5,maximum-scale=0.5, minimum-scale=0.5,user-scalable=no" />
---- 本文结束,感谢您的阅读 ----