# 移动端适配

# 基本概念

物理像素(DP)

物理像素又称设备像素/设备物理像素,就是组成屏幕的一个个真实的物理像素点,比如屏幕的分辨率是 1920 * 1080 ,说明这个屏幕就是由真实的 1920 * 1080 个像素点组成,从出厂那一刻就不会改变了。物理像素就是分辨率,同一个尺寸下可以有不同的分辨率。

CSS像素

CSS像素是一个相对单位,它并不是完全等于物理像素,只是表示一个最小基本单位的抽象概念。

一般默认情况下一个CSS像素等于一个物理像素,在多倍屏下会相当于多个物理像素。

设备独立像素(DIP)

设备独立像素和CSS像素一样,表示一个最小的基本单位,也被称为逻辑像素。一个抽象的独立像素代表多少物理像素。

设备独立像素 = CSS 像素 = 逻辑像素 = 屏幕真实物理尺寸 = 设计稿尺寸

设备像素比(DPR)

设备像素比描述的是未缩放状态下,物理像素和设备独立像素的初始比例关系。表示一个设备独立像素或者一个CSS像素可以容纳多少个物理像素。

DPR = 物理像素 / 设备独立像素(CSS像素)

在不同的屏幕上,CSS像素所呈现的物理尺寸是一致的,而不同的是CSS像素所对应的物理像素具数是不一致的。在普通屏幕下1个CSS像素对应1个物理像素,而在Retina视网膜屏幕下,1个CSS像素对应的却是4个物理像素。

获取DPR:window.devicePixelRatio 或者媒体查询 min-device-pixel-ratio

window.devicePixelRatio
@media only screen and (-webkit-min-device-pixel-ratio:2){}

# 视口

三个视口

  • 布局视口:网页布局的基准窗口,PC默认等于浏览器窗口,移动端有一个默认值为980px,为了让网页布局可以在手机上显示。可以通过调用document.documentElement.clientWidth / clientHeight来获取布局视口大小。

  • 视觉视口:用户通过屏幕真实看到的区域。如果用户进行缩放则调整的是视觉视口,而不是布局视口。可以通过调用window.innerWidth / innerHeight来获取视觉视口大小。

  • 理想视口:就是让页面布局正常,不需要缩放什么的就能正常在手机上显示的视口。一般为屏幕尺寸,可以通过调用screen.width / height来获取理想视口大小。

利用meta标签对viewport进行控制

一般,移动端都需要设置meta标签来得到理想视口让页面正常显示。

移动端默认的viewport是布局视口,我们可以通过设置meta来设置理想视口。

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

该meta标签设置viewport的宽度等于设备的宽度,同时不允许用户手动缩放。也许允不允许用户缩放不同的网站有不同的要求,但让viewport的宽度等于设备的宽度,这样就得到了理想视口,如果你不这样的设定的话,那就会使用那个比屏幕宽的默认视觉视口,也就是说会出现横向滚动条。

width=device-widthinitial-scale=1 设置哪个都可以得到理想视口。width=device-width让布局视口宽度等于设备宽度,得到理想视口;initial-scale=1 是相对于理想视口进行缩放的,不会改变布局视口的大小,所以页面布局是不变的,但是缩放会改变视觉视口的大小。布局视口取width的宽度和视觉视口的最大值。

页面的缩放系数 = CSS像素 / 设备独立像素

页面的缩放系数 = 理想视口宽度 / 视觉视口宽度

页面进行缩放时,改变了视觉视口,同时也改变了CSS像素所包含的真实物理像素,缩放越大,CSS像素跨越的物理像素越多,显示的越大; 缩放越小,CSS像素跨越的物理像素越少,显示的越小。

根据设备的dpr动态改变meta标签的缩放比,让布局视口等于真实分辨率大小,也就是让1CSS像素等于1物理像素,这样也可以对移动端进行适配。这样既设置了理想视口,也解决了1px问题。

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

# 使用lib-flexible适配

由于 viewport 单位得到众多浏览器的兼容,lib-flexible 这个过渡方案已经可以放弃使用,不管是现在的版本还是以前的版本,都存有一定的问题。建议大家开始使用 viewport 来替代此方案。

原理

不论哪个版本都是以宽度的1/10设置html的font-size的,所以只要根据设计稿的1/10作为1rem换算尺寸就行了。

var width = window.document.documentElement.getBoundingClientRect().width;
var rem = width / 10;
window.document.documentElement.style.fontSize = rem + 'px';

旧版本使用

1、使用时,html 中不需要添加 view-port 的 meta 标签,会自动加上。

2、引入flexible

在head头部引入lib-flexible.js,不要设置meta视口,执行这个js后,会在html上增加一个data-dpr属性,以及设置font-size大小。之后页面中的元素,都可以用rem单位来设置。html上的font-size就是rem的基准像素。

3、把视觉稿中的px转换成rem

我们以设计稿的1/10为1rem,整个宽度是10rem,以设计稿750为例,1rem为设计稿的1/10为75px。

1rem = 75px

因此,对于视觉稿上的元素的尺寸换算,只需要原始px值除以rem基准px值即可。例如240px * 120px的元素,最后转换为3.2rem * 1.6rem。

4、字体不使用rem的方法

字体的大小不推荐用rem作为单位。所以对于字体的设置,仍旧使用px作为单位,并配合用data-dpr属性来区分不同dpr下的的大小。

div {
  width: 1rem; 
  height: 0.4rem;
  font-size: 12px; // 默认写上dpr为1的fontSize
}

[data-dpr="2"] div {
  font-size: 24px;
}

[data-dpr="3"] div {
  font-size: 36px;
}

使用less或者sass 动态显示字体大小

// 适配dpr的字体大小
@mixin font-dpr($font-size){
  font-size: $font-size;

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

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

/* 使用 */
@include font-dpr(14px);

2.0使用

安装:

npm i -S amfe-flexible

引入:

<!-- 要手动引入meta了 -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<script src="./node_modules/amfe-flexible/index.js"></script>

引入后也是以设计稿1/10作为1rem来进行换算就行了,字体其他的没有要求,相比较旧版本较为简单。

flexible布局缺陷

  • 1、对高倍屏的安卓手机没做处理,全部按照 dpr = 1 处理,现在许多安卓机都是高倍屏,已经不兼容了。
  • 2、不兼容响应式布局
  • 3、不兼容 iframe、video

flexible适配方案只是早些时期的过渡方案,所以现在flexible官方也不建议使用了,推荐使用viewport方案来适配。

# Viewport方案

  • vw(Viewport's width):1vw等于视觉视口的1%
  • vh(Viewport's height) : 1vh 为视觉视口高度的1%
  • vmin : vw 和 vh 中的较小值
  • vmax : 选取 vw 和 vh 中的较大值

首先添加meta标签设置视口

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

然后把设计稿尺寸转化成对应的vw/vh就可以了。

px转vw

在使用less/sass预编译语言时可以借助其变量来配合转译尺寸,不需要手动计算,但也不是很方便,我们可以借助 postcss-px-to-viewport 插件来帮助转译。

# 适配问题

# 1px问题

因为多倍屏的存在,我们所写的1px CSS像素被多个真实的物理像素渲染,所以1px就会显得很粗。

flexible.js通过缩放比例让1px CSS像素等于真实的物理像素解决了1px问题,但是vw没办法动态设置缩放比例,所以就会有1px问题。

解决办法: 0.5像素边框

参考链接:

使用Flexible实现手淘H5页面的终端适配 (opens new window)

关于移动端适配 (opens new window)

移动端开发的屏幕、图像、字体与布局的兼容适配 (opens new window)

CSS像素、物理像素、逻辑像素、设备像素比、PPI、Viewport (opens new window)