# 移动端适配
# 基本概念
物理像素(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-width
和 initial-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)