我不知道大家平时有没有这样以下两个疑问
一. 从像素开始搞懂移动端适配
1.经常听到手机发布会说的iPhone X参数,5.8寸屏幕,屏幕分辨率:2436x1125像素,458 ppi,我们在Chrome手机开发者模式的时候iPhone X分辨率又是375 x 812,大家是不是都傻傻分不清楚这些都是些啥概念。
2.为什么同样的图像能够在不同屏幕(1080p,2k,4k)展现出现不大的差异,分辨率高的屏幕看起来同样的图像为什么很清晰很清晰,甚至外接4k显示器的屏幕,写代码要放大字体2倍来看?
只有解决以上两个疑问,我们才真真理解屏幕的差异化,才能做好移动端适配
1. 物理像素(设备像素pt)
物理像素也叫设备像素,是屏幕制造出来就已经固定的,单位是pt。每个屏幕都是由很多发光的二极管组成,通过不同颜色发光的二极管排列展示成不同的图像,如下图所示。通过调整发光二极管你可以改变屏幕图像的颜色和亮度。每一个发光的二极管都被当作为一个物理像素点,上面疑问1iPhone X发布会说的2436x1125,就是硬件物理像素。
2.逻辑像素(CSS像素px)
- 由于厂商不同,屏幕大小不同,我们不能直接用物理像素来做图像显示,这样例如400的物理像素宽的设备和2000的物理像素设备相比,同时看400pt腾讯视频画面那不是差距很大很大,一个只占到1/5内容不到,一个刚合适。但是现实中你发现差异并不大,只是整体变小了而已。为了兼容这些差异,提出了逻辑像素的概念.
- 具体怎么实现了的呢,首先是在小屏幕400pt的设备上,使用1px=1pt,然后在大屏幕1px=5pt的形式,那这样400px的画面岂不是很完美的展示了,上面疑问1说的375 x 812就是逻辑像素,用于磨平设备之间差异所规定的,所以chrome上你调整很多手机的时候,他差异很小的原因都是3,400左右,这就是近似的磨平了物理像素差距过大
3.设备像素比(DPR)
设备像素比(DevicePixelRatio):DPR = 设备像素 / CSS 像素 例如,iPhone 6 物理像素为 750 x 1334,理想视口 375 x 667 ,DPR = 2 看了物理像素和逻辑像素的解释后,有些人会说手机物理分辨率甚至比电脑还要高,根本不是上面说的那样。始终要理解到物理像素跟设备大小不是直接关系,手机可能物理像素就是3000宽,但是很多大电脑才2000个物理像素宽,所以电脑上1px=1pt,手机上这么多分辨率不可能一样展示就展示的很小很小的内容了,就使用1px=4pt,逻辑像素比4,通过这个解释主要是让大家走出一个误区,屏幕大小跟分辨率没直接关系
设备像素比越高,就是DPR越高的屏幕,他展示那种设计师给的二倍图,三倍图拥有更多像素的图片吗,高清屏幕展现得更加细腻,屏幕显示差距就出来了,苹果的retina屏幕比高清屏多很多像素,所以都是2,3个像素显示1px,也就是平时开发过程中H5只能操作px,不能像安卓开发操作pt的原因。
代数 | 设备 | 操作系统 | 逻辑分辨率(point) | 物理分辨率(pixel) | 屏幕尺寸(对角线长度) | 缩放因子 |
---|---|---|---|---|---|---|
iPhone | ||||||
第十一代 | iPhone 8 | iOS 11 | 375 x 667 | 1334 x 750 | 4.7寸 | 2x |
第十一代 | iPhone 8 Plus | iOS 11 | 414 x 736 | 2208 x 1242 (1920x1080) | 5.5寸 | 3x |
第十一代 | iPhone X | iOS 11 | 375 x 812 | 2436 × 1125 | 5.8寸 | 3x |
4.PPI
PPI即每英寸所拥有的像素数目。pixels per inch所表示的是每英寸对角线上所拥有的像素(pixel)数目。
那么如何计算呢,首先知道疑问1中的发布会设备多少英寸,你买手机的时候发现iPhone X 5.5英寸拿到手咋个比iphone8 plus5.5英寸小这么多。因为他的屏幕尺寸指的屏幕的对角线长度,iPhone8-iPhoneX边框等等全变窄了,所以对于这种不要觉得疑惑。
2是算对角线的物理像素
PPI=√(X^2+Y^2)/ Z (X:长度像素数;Y:宽度像素数;Z:屏幕大小)。
iphonex ppi=√(2436^2+1125^2)/ 5.8=460左右的ppi
5.UI设计像素
平时我们使用psd等等设计图上面展示的像素是设计师根据屏幕不同做的设计图,比如移动端使用375的宽标准,PC端使用1920的宽,这样标准的设计图能更好的兼容设备,所以我们会根据UI设计图来做标准适配
6. 相对像素 em 和 rem
对于px是逻辑像素,他是绝对像素单位,不能根据设备来展示变化,虽然逻辑像素大部分被兼容到300-400,比如ipad是超过400的,电脑中1px=1pt,也就是1920等,差距就更大了,淘宝就是根据不同分辨率做了不同的设计图,不同放大缩小来处理。 对于想一套代码处理多种设备的,相对像素诞生了
-
相对像素 em。em 这个单位是相对于父元素的 font-size 字号值,实际就是父元素 font-size 的几倍。这个字号可以是 CSS 指定的,也可以是从父元素的父元素继承来的,如果前两个都没有,就会使用默认值(一般是16px)。假如父元素指定了
“font-size:20px;”
,子元素使用了“width: 10em”
,那么子元素的实际宽度就是 200px。使用em单位注意html的继承关系,因为他是相对于父集来做的,比较容易错乱 -
相对像素 rem。rem相对于em的不同是,它是相对于根节点,也就是 HTML 节点的 font-size 属性指定的 px 值。页面里所有用 rem 作为长度单位的元素,就都以 HTML 为准,这样的好处是不会由于层级而导致错乱 把像素搞清楚了,后面我会循序渐渐地更新具体的屏幕适配方案
二. 多种方案一次性搞定移动端适配
理解viewport
移动端和PC端不同,PC端的由于大部分是1px=1pt,也就是一般PC逻辑分辨率超过很大,对应到展示在375左右的移动端设备上,内容肯定一屏幕展示不完的,这个时候肯定要有横向滚动条来展示全部内容,移动端就提出了两个窗口概念
视口当前可见的部分叫做可视视口(visual viewport) 。可视视口可能会比布局视口(layout viewport )更小,他们的关系如下
可视视口(屏幕展示的区域)+横向滚动条的区域=布局视口
viewport指的就是展示网页的区域,一般浏览器会将viewport设置980大于可视区域,这样就出现横向滚动条了,如下就是一般浏览器的默认viewport宽度
为了印证这个猜想,平时创建html文件的时候,你很可能都没去管meta的viewport标签,快捷键创建html,他会自动带上,然后打开chrome调试模式,如下
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>带有viewport</title>
<style>
html,body{
background:red;
}
</style>
</head>
<body>
</body>
</html>
效果如下
如果去掉<meta name="viewport" content="width=device-width, initial-scale=1.0">
,设置了viewport为视口宽度为设备宽度,所以可视窗口=布局窗口。取消了这个设置,就会展示默认的viewport宽度
关于viewport的具体参数,MDN上面很少介绍,参考三方网站详细表
width | 设置layout viewport 的宽度,为一个正整数,或字符串"width-device" |
---|---|
initial-scale | 设置页面的初始缩放值,为一个数字,可以带小数 |
minimum-scale | 允许用户的最小缩放值,为一个数字,可以带小数 |
maximum-scale | 允许用户的最大缩放值,为一个数字,可以带小数 |
height | 设置layout viewport 的高度,这个属性对我们并不重要,很少使用 |
user-scalable | 是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes代表允许 |
其实主要平时用到的属性就如vscode插件自动生成的两句,仔细想为什么要写这两句,width=device-width
设置视口宽度=布局宽度,但是在iphone和ipad上,无论是竖屏还是横屏,宽度都是竖屏一样的宽度。使用initial-scale=1.0
是什么意思呢,就是将网页缩放到和视口宽度的100%,但这次轮到了windows phone 上的IE 无论是竖屏还是横屏都把宽度设为竖屏时的宽度。这个时候写两句的话,他就会选择最大的起效,兼容了所有设备
<meta name="viewport" content="width=device-width, initial-scale=1.0">
1.纯缩放百分比适配
假如一行四个div元素
div{
width:25%
}
这种方式在bootstrap等响应式框架用的多,这样的优点有
- 根据大屏幕,小屏幕都可以直接百分比,不会出现很大留白,显得页面极其不平衡
- 不用关心px等宽度具体是多少,会自动适配
- 不存在兼容性问题
缺点有
- 不同屏幕宽度差距过大会造成页面按钮内容变形。
- 完全相对于父元素,元素脱离文档流就会造成页面混乱
2.多套代码分端适配
这种采用媒介的形式,css提供了@media用于书写分段样式
@media only screen and (min-width: 300px) {
.main {
width: 60px;
}
}
@media only screen and (min-width: 400px) {
.logo {
width: 80px;
}
}
优点:
- 显示更加灵活,比如一行在300px下面展示3个按钮,在400px就可以手动改为4个按钮 缺点
- 需要维护多套设计图和多套CSS样式,维护难度大
3.flex和grid弹性布局适配
弹性布局是根设备宽度自适应撑满屏幕,根据屏幕自适应展示几个按钮,这种模式可以小范围适配,对于移动端H5适配还是要配合rem模式,并且兼容性不是很好。
4.rem适配
使用rem相对单位,例如根元素设置20px,那么1rem=20px,后续所有单位都根据根元素做换算,30px就是1.5rem,这种方式的优点如下
- 可以通过改变根元素font-size按比例适配一个页面
- 灵活根据尺寸自定义根节点的rem值大小 当然有缺点就是
- 还是需要配合@media进行设置不同尺寸font-size的rem大小,不过rem配合@media已经是比较主流的方案了
5.使用rem+vw适配
为了做好浏览器的适配方案,参考网上的设计总结了一套方案
首先移动端的设计图都是相对于width:750px的标准设计图适配,第一步我们设定
100vw=750px
那么
1px=0.1333vw
然后第二步设定
设定
1rem=100px
7.5rem=750px
那么
1rem=100px=13.33vw
第三步采用@media做简单的区间适配
html {
font-size: 13.33333vw;
}
@media screen and (max-width: 300px) {
html {
font-size:1rem;
font-size: 13.33333vw;
}
}
@media screen and (min-width: 300px) and (max-width: 400px) {
html {
font-size:1rem;
font-size: 13.33333vw;
}
}
...根据适配屏幕多少操作
然后由于真实计算出每个屏幕的rem对应的px是多少
html {
font-size: 13.33333vw;
}
@media screen and (max-width: 300px) {
html {
font-size: 40px;//根据真实屏幕逻辑像素所计算出来的
font-size: 13.33333vw;
}
}
@media screen and (min-width: 300px) and (max-width: 400px) {
html {
font-size: 53.32px;//根据真实屏幕逻辑像素所计算出来的
font-size: 13.33333vw;
}
}
注意vw只能在安卓4.0和ios8以上使用
三. 企业级移动端适配3种方案实操
1. 开端
对于前两节像素,和实现方案的理解,我们总结了px+rem,px+vw,rem+vw三种模式的互相转换的方案,这一节我准备结合这三种如何转换和项目实操进行结合。
首先我们要明白一点,设计图始终是有一个以固定px为设计图(750xauto),或者有些设计师是以(375xauto)这种直接二倍图就可以了。然后将px转换为rem适配或者vw适配。
2. px+vw
这是网上推荐的比较多的方式
1.创建vue项目
npm i @vue/cli -g
vue create px-vw
2.安装 postcss-px-to-viewport 这个插件很方便的将px转换成vw
npm i postcss-px-to-viewport --save-dev
3.配置postcss-px-to-viewport插件进行配置文件.postcssrc.js
module.exports = {
plugins: {
autoprefixer: {}, // 用来给不同的浏览器自动添加相应前缀,如-webkit-,-moz-等等
"postcss-px-to-viewport": {
unitToConvert: "px", // 要转化的单位
viewportWidth: 750, // UI设计稿的宽度
unitPrecision: 4, // 转换后的精度,即小数点位数
propList: ["*"], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
viewportUnit: "vw", // 指定需要转换成的视窗单位,默认vw
fontViewportUnit: "vw", // 指定字体需要转换成的视窗单位,默认vw
selectorBlackList: [""], // 指定不转换为视窗单位的类名,
minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
mediaQuery: true, // 是否在媒体查询的css代码中也进行转换,默认false
replace: true, // 是否转换后直接更换属性值
exclude: [/node_modules/], // 设置忽略文件,用正则做目录名匹配
landscape: false, // 是否处理横屏情况
},
},
};
4.在app.vue书写简单适配环境
<template>
<div class="app">
<div class="header"></div>
<div class="content"></div>
<div class="footer"></div>
</div>
</template>
<script>
export default {};
</script>
<style>
* {
margin: 0;
padding: 0;
}
.app {
width: 750px;
height: 1000px;
background: red;
}
.header {
width: 400px;
height: 200px;
background: black;
}
.content {
width: 300px;
height: 200px;
background: yellow;
}
.footer {
width: 200px;
height: 200px;
background: gray;
}
</style>
375 x auto 540 x auto
5.总结 这种模式实现起来非常简单
3. px+rem
对于rem我们知道是相对于根节点的多少倍数,那么我们只需要在根节点设置font-size为真实设备宽度的逻辑像素px,然后所有内容相对于根节点写多少倍rem即可
1.根据屏幕换算根节点的font-size
定义375 x auto的屏幕为font-size:14px
那么
width为屏幕宽度 fontSize = 14 * (width / 375) + "px";
2.根据屏幕变化更改响应式根节点计算
// 基准大小
const baseSize = 28;
// 设置 rem 函数
function setRem() {
// 当前页面宽度相对于 750 宽的缩放比例,可根据自己需要修改。
const proportion = document.documentElement.clientWidth / 750;
// 设置页面根节点字体大小
document.documentElement.style.fontSize = baseSize * proportion + "px";
}
// 初始化
setRem();
// 改变窗口大小时重新设置 rem
window.onresize = function () {
setRem();
};
3.px-rem
经过步骤2的操作,我们设置了准确的根节点font-size,那么接下来进行的操作
经过步骤3的操作,我们自动设置了根节点的值,然后页面可以使用rem来书写代码,但是设计图都是750px的格式,里面所有量出来的尺寸都需要自己计算后写成rem,这显然是不行的,那么我们还需要将28px按比例转成1rem,我们设置的基准是750尺寸,28px(也就是真实逻辑分辨率375,14px)
4.安装postcss-pxtorem
npm install postcss-pxtorem -D
5.配置.postcssrc.js
具体参数意思可以参考上面说到的px+pw适配方式里面有讲解
module.exports = {
plugins: {
autoprefixer: {}, // 用来给不同的浏览器自动添加相应前缀,如-webkit-,-moz-等等
"postcss-pxtorem": {
rootValue: 28,
propList: ["*"],
},
},
};
总结:这时候已经转换成功了,px按比例转成rem,rem根节点安设配逻辑像素转成px
4.rem+vw
这种模式主要是解决兼容性问题,方案一直接转换成vw可能导致兼容性问题,这个时候我们需要做一个补丁,相当于设置了vw之前也加一层font-size具体px的设置,具体如何计算参考我的上一篇文章所提到的。
html {
font-size: 13.33333vw;
}
@media screen and (max-width: 300px) {
html {
font-size: 40px;//根据真实屏幕逻辑像素所计算出来的
font-size: 13.33333vw;
}
}
@media screen and (min-width: 300px) and (max-width: 400px) {
html {
font-size: 53.32px;//根据真实屏幕逻辑像素所计算出来的
font-size: 13.33333vw;
}
}
这样根rem计算成功了,然后就是方案二一致的px转成rem就行了,这种方案我这边认为是比较完美的。