移动端适配指南

CSS 像素

虚拟像素,可以理解为“直觉”像素,CSSJS使用的抽象单位,浏览器内的一切长度都是以CSS像素为单位的,CSS像素的单位是px

物理像素 (physical pixels)

物理像素(设备像素),顾名思义,显示屏是由一个个物理像素点组成的,通过控制每个像素点的颜色,使屏幕显示出不同的图像,屏幕从工厂出来那天起,它上面的物理像素点就固定不变了,单位 pt

设备独立像素 (density-independent pixel)

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

设备像素比 DPR(device pixels ratio)

设备 CSS像素 devicePixelRatio(设备像素/CSS像素)
iPhone 5/SE (1136x640) 568*320 2
iPhone 6/7/8 (1334 x 750) 667*375 2
iPhone 6/7/8 Plus (1920 x 1080) 736*414 3
iPhone X (2436 x 1125) 812*375 3
Pixel 2 731*411 2.625

主要思想

  • 根据dpr的值来修改viewport实现1px的线
  • 根据dpr的值来修改htmlfont-size,从而使用rem实现等比缩放
  • 使用Hack手段用rem模拟vw特性

实例

750px设计稿75px的按钮,10%

不设置默认viewport, 默认980px

例1:仅设置width=device-width,视口宽度为设备宽度

iPhone 5/SE (1136x640) => 75/320 = 23.4%

iPhone 6/7/8 (1334 x 750) => 75/375 = 20%

iPhone 6/7/8 Plus (1920 x 1080) => 75/414 = 18.1%

例2:动态设置 scale,比如缩放0.5倍,则视口宽度 375*2=750px

iPhone 5/SE (1136x640 dpr:2) => scale=0.5 => 75/640 = 11.7%

iPhone 6/7/8 (1334 x 750 dpr:2) => scale=0.5 => 75/750 = 10%

iPhone 6/7/8 Plus (1920 x 1080 dpr:3) => scale=0.33(0.5) => 75/1242(828) = 6(9)%

px to rem

整个宽度为 10rem,html 对应的 font-size 为 75px

1
2
3
4
var docEl = doc.documentElement;
var width = docEl.getBoundingClientRect().width;
var rem = width / 10;
docEl.style.fontSize = rem + 'px';

例3:动态设置 scale,fontSize 被设置为 750 / 10 = 75px,即 1rem 等于 1/10 屏幕宽度

iPhone 5/SE (1136x640 dpr:2) => scale=0.5 => 75/640 = 11.7% => 640 / 10 = 64px

iPhone 6/7/8 (1334 x 750 dpr:2) => scale=0.5 => 75/750 = 10% => 750 / 10 = 75px

iPhone 6/7/8 Plus (1920 x 1080 dpr:3) => scale=0.33(0.5) => 75/1242(828) = 6(9)% => 1242 / 10 = 124.2px

vw & vh

  • vw: 等于视口宽度的1%
  • vh: 等于视口高度的1%
  • vmin: 选取 vw 和 vh 中最小的那个
  • vmax: 选取 vw 和 vh 中最大的那个

使用vw来实现页面的适配,并且通过PostCSS的插件 postcss-px-to-viewportpx转换成vw。这样的好处是,我们在编码的时候,只需要根据设计图写px单位

不足

当容器使用 vw 单位, margin 采用 px 单位时,很容易造成整体宽度超过 100vw,从而影响布局效果。对于类似这样的现象,我们可以采用相关的技术进行规避。比如将 margin 换成 padding,并且配合 box-sizing。只不过这不是最佳方案,随着将来浏览器对 calc() 函数的支持之后,碰到 vwpx 混合使用的时候,可以结合 calc() 函数一起使用,这样就可以完美的解决。

另外一点,px 转换成 vw 单位,多少还会存在一定的像素差,毕竟很多时候无法完全整除。

1px 问题

viewport + rem

同时通过设置对应 viewport 的 rem 基准值,这种方式就可以像以前一样轻松愉快的写1px了。

在devicePixelRatio = 2 时,输出 viewport:

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

在devicePixelRatio = 3 时,输出 viewport:

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

伪类 + transform

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.hairlines li {
position: relative;
border: none;
}
.hairlines li:after {
content: '';
position: absolute;
left: 0;
background: #000;
width: 100%;
height: 1px;
transform: scaleY(0.5);
transform-origin: 0 0;
}

CSS 实现长宽比

垂直方向的padding

padding-toppadding-bottom 的百分比值是根据容器的 width 来计算的。

1
2
3
4
5
6
7
8
9
10
11
12
13
.aspectration {
position: relative; /*因为容器所有子元素需要绝对定位*/
height: 0; /*容器高度是由padding来控制,盒模型原理告诉你一切*/
width: 100%;
}
.aspectration[data-ratio="16:9"] {
padding-top: 56.25%;
}
.aspectration[data-ratio="4:3"] {
padding-top: 75%;
}

vw

1
2
3
4
.aspectration[data-ratio="16:9"] {
width: 100vw;
height: 56.25vw;
}

aspect-ratio

未来的方案:长宽比属性 aspect-ratio

1
2
3
4
5
6
7
8
9
.aspectration[data-ratio="16:9"] {
width: 100%;
height: aspect-ratio(16/9);
}
// or
.aspectration[data-ratio="16:9"] {
width: 100%;
aspect-ratio: calc(16/9);
}

PostCSS

1
npm i postcss-aspect-ratio-mini postcss-px-to-viewport postcss-write-svg postcss-cssnext postcss-viewport-units cssnano

配置 .postcssrc.js 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module.exports = {
'plugins': {
'postcss-import': {},
'postcss-url': {},
'postcss-aspect-ratio-mini': {},
'postcss-write-svg': {utf8: false},
'postcss-cssnext': {},
'postcss-px-to-viewport': {
viewportWidth: 750, // (Number) The width of the viewport.
viewportHeight: 1334, // (Number) The height of the viewport.
unitPrecision: 3, // (Number) The decimal numbers to allow the REM units to grow to.
viewportUnit: 'vw', // (String) Expected units.
selectorBlackList: ['.ignore', '.hairlines'], // (Array) The selectors to ignore and leave as px.
minPixelValue: 1, // (Number) Set the minimum pixel value to replace.
mediaQuery: false // (Boolean) Allow px to be converted in media queries.
},
'postcss-viewport-units': {},
'cssnano': {
preset: 'advanced',
autoprefixer: false,
'postcss-zindex': false
}
}
};

参看资料:

使用Flexible实现手淘H5页面的终端适配

再聊移动端页面的适配

如何在Vue项目中使用vw实现移动端适配

CSS实现长宽比的几种方案

CSS像素、物理像素、逻辑像素、设备像素比、PPI、Viewport