H5 移动端 开发的必不可少的一个环节就是 移动端网页的适配,因为 UI 通常只会提供大小固定的设计稿,而各种不同移动设备具有不同的页面分辨率和大小,所以适配的目的就是让一份设计稿在不同移动设备上表现出一致性。
虽然现如今各种插件都可以帮助我们快速配置完成,例如 lib-flexible
、postcss-pxtorem
、postcss-px-to-viewport
等等,但不少小伙伴在被问及相关原理时却很难说清楚,那么本篇文章我们就一起来探究一下其中原理吧!!!
CSS 中的尺寸单位
在了解具体的适配方案之前,我们先把 CSS 中适配会涉及到的相关尺寸单位进行一个了解吧,总结起来就是一句话:CSS 中的尺寸单位都是 相对长度单位
,只是相对的目标不同。
px 像素单位
px 全称为 pixel(像素
),它是相对于 屏幕显示器分辨率(桌面设定的分辨率,不是显示器的物理分辨率
) 而言的,在 相同/不同 的设备上 1px 表示多个 设备像素。
当 一个像素点越大 时, 呈现的图像就会 越模糊;当一个像素点越小时, 像素点就会 越密集, 呈现的图像就会 越清晰。
em 相对单位
好多人都认为 em 就是相对于 父元素 font-size,实际上在不同的 CSS 属性当中使用 em 其相对的目标也是不同,如下:
1、用于 font-size 中则是相对于 父元素 font-size 大小
2、用于 其他属性(如 width,height) 中使用是相对于 自身 font-size 大小
值得注意的是,若当前元素/父元素 的 font-size 未设置,由于 font-size 属性值可被继承的原因,可逐级向上查找,最终找不到则相对于浏览器默认字体大小,即 font-size = 16px。
rem (root em) 相对单位
rem 是 CSS3 新增的一个相对单位,它只相对于 根元素 html 的 font-size 字体大小,rem 与 em 的区别在于:
-
rem
相对于html
根元素的,因此在body
标签里面设置font-size
是不起作用的 -
因此
rem
就可做到 目标元素 与 根元素 间保持 成比例 的大小关系,又可以避免字体大小逐层复合的连锁反应等,例如公共的字体大小可以在body
中设置即可
vw 和 vh
vw 全称是 viewport width,代表的是 视口的宽度,相对于 视口 viewport 的 宽度
vh 全称是 viewport height,代表的是 视口的高度,相对于 视口 viewport 的 高度
vw 和 vh 是将 视口 宽/高 都分成 100 份,因此 100vw = 视口宽
、100vh = 视口高
与之相关的还有
vmin
和vmax
两个单位
-
vmin 和 vmax 代表的是 视口宽度 和 视口高度 中的 最小值 和 最大值
-
vmin
= 视口高度vh
和 宽度vw
间的最小值 -
vmax
= 视口高度vh
和 宽度vw
间的最大值
适配方案
rem 适配(等比适配)
核心原理
-
将 设备视口 划分成 n 份, n 可以是 任何正确的值(如
flexible.js
中的n = 10
)-
设置 设备视口
根元素 html
的font-size = 设备视口宽 ÷ 份数 n
,即得到 设备视口1
rem 到底表示 多少设备视口 px
-
-
将 设计稿 也同样划分成 n 份,此时 设计稿中的
a
px 对应 设备视口b
rem 的计算方式为-
设备视口
b
rem = 设计稿a
px ÷ (设计稿宽
÷n
份)
-
举个例子
设备视口宽为 375px
-
将设备视口分成 10 份,设置
根元素 html 的 font-size
=375
÷10
=37.5
px,即1
rem =37.5
px
(function (n = 10){
const dEl = document.documentElement;
function setRem(){
const rem = dEl.clientWidth / n;
dEl.style.fontSize = rem + 'px';
}
// 初始化执行
setRem()
// 视口大小变动时执行
window.onresize = setRem
})()
设计稿宽为 750px
-
将 设计稿 也分成 10 份,每份大小 =
750
÷10
=75
px -
此时
设计稿
上的"点我拍照"(font-size: 34px)
的文案转换成符合设备视口
对应的rem
就为:-
设备视口 中
文字 font-size
=34 ÷ 75
=0.4533333333333333
rem ≈0.45
rem
-
【
注意
】 一般计算结果(人为计算、插件自动化自动化计算
)都不会使用这么长的小数位,比如
0.4533333333333333
rem ≈0.45
rem,此时:
原始值
0.4533333333333333
rem =0.4533333333333333
*37.5
=17
px保留两位小数后的值
0.45
rem =0.45
*37.5
=16.875
px而在肉眼观察下
16.875
px 和17
px 是没有差别的,因此可以忽略不计
用 amfe-flexible 和 postcss-pxtorem 验证
-
文本和原始样式
-
amfe-flexible
和postcss-pxtorem
配置 -
最终展示效果
早期 rem 适配优化(过期方案,了解即可
)
在早期还没有各种 CSS 预处理器 和 自动化计算的插件 时(即 还需要人为计算时
),上述 rem 适配的方案 有一个缺点,那就是针对 设计稿中不同的 px 的转换为 设备视口 rem 的计算过程还是比较繁琐,特别是一些比较难算的数值。
例如,上述将 设计稿 34px
转换为 设备视口 0.9rem
的例子,对大部分人来说是不能 直接/快速 看到设计稿中 px 的数值就能直接算出其对应的 rem 单位的数值的,因此需要进行优化。
而 优化 的核心就是 将 复杂计算
变成 简单计算
,例如在 宽 750 px 的设计稿中:
-
34px
转换成 rem =34
÷75
,这种不直观的计算叫 复杂计算,若可以实现34
÷100
即可得到相应的 rem 的方式,就称为 简单计算(任何数除以 10 或 100 都很容易口算
)
也就是说,如果我们希望所有设计稿上 px 的计算都是 简单计算,那么我们就需要保证其 渲染结果 是正确的即可,换句话说,就是我们需要调整设备视口的 1 rem = n px 中 n 的值让其能够满足 34
÷ 100
= 0.34
rem 的情况下还能够被正常渲染为 17
px 即可,这无非就涉及到一个数学倍数计算而已,就不过多展开了。
vw/vh 适配
前面说过,vw 和 vh 是将 视口 宽/高 都分成 100 份,相对于 设备视口 375px 来说 1vw = 375 ÷ 100 = 3.75px
。
经过了 rem 适配方案 的介绍,vw/vh 这种适配方式就是相当于把替换了原本的 rem 单位,因此,这个方式的计算方式和 rem 的方式 如出一辙,区别就在于:
-
rem
的适配方式支持自定义将设备视口划分为n
份,n
可以是任何正确值 -
vw/vh
就是将设备视口划分为100
份,不支持自定义
因此,假设将 设计稿 750px 中的 34px 转换为 设备视口 n vw 就等于 n
= 34
÷ 7.5
= 4.533333333333333
vw ≈ 4.53
vw。
CSS 预处理器 — 简化计算
如果使用的是 CSS 预处理器(Less、Sass),那么就可以通过定义一个全局的 函数 来帮助我们进行运算,例如:
-
Less 中 px2vw() 的定义:
// plugin.js
module.exports = {
install: function (less, pluginManager, functions) {
functions.add('px2vw', (param, perVW) => {
if (!param.value) return '0vw'
if (!perVW.value) return param.value + 'px'
return Number(param.value) / perVW.value + 'vw'
})
},
}
// 具体使用
<style lang="less">
@plugin './plugin.js';
@design-width: 750;
@per-vw: @design-width / 100;
.text {
font-size: px2vw(34, @per-vw);
color: #457fff;
}
</style>
-
Sass 中 px2vw() 的定义:
<style lang="scss">
$design-width: 750;
$per-vw: $design-width / 100;
@function px2vw($param) {
@return $param / $per-vw + 'px';
}
.text {
font-size: px2vw(34);
color: #457fff;
}
</style>
使用 postcss-px-to-viewport 进行验证
-
文本和原始样式
-
postcss-px-to-viewport
配置 -
最终展示效果
等比缩放 — viewport <meta>
标记
所谓 等比缩放 也就是我们不需要关注 设计稿 px
到底对应多少 设备视口 rem
或 vw
,在开发时,直接使用 设计稿提供的数据 px
即可,然后在将整体页面按照 设备视口 / 设计稿 的比例进行 整体缩放。
而缩放就需要使用到 使用 "viewport" <meta>
标记 来控制视口的大小和形状了,例如常见的 <meta>
标记如下:
很明显,我们只需要控制其中的 width
和 initial-scale
的值即可,它们分别是代表当前设备视口宽度和缩放比的值。
还是用 设计稿 750px 和 设备视口 375px 举例子,因为我们是直接只用设计稿提供的数据来开发,那么上面的 width = 设计稿的宽
、initial-scale = 设备视口 / 设计稿
。
(function (designWidth) {
const dEl = document.documentElement;
let meta = document.querySelector("meta[name=viewport]");
// 页面中不存在 <meta name="viewport" /> 时,手动创建一个
if(!meta) {
meta = document.createElement('meta');
meta.setAttribute('name', 'viewport');
document.head.appendChild(meta);
}
function setMetaContent(){
const deviceWidth = dEl.clientWidth;
const scale = deviceWidth / designWidth;
const content = `width=${deviceWidth}, initial-scale=${scale}`;
meta.setAttribute("content", content);
}
setMetaContent();
window.addEventListener("resize", setMetaContent)
})(750);
第三方组件库如何做适配?
不知道你是不是会有一些疑问,比如第三方组件库的适配方式和我们使用的不一致怎么办?
不用担心,第三方组件库默认都是直接使用 px 的方式来开发的,因此我们在项目中定义的适配方式就是第三方组件库的适配方式,不会产生冲突的。
例如,vant-ui 文档中很贴心的为你列出了 浏览器适配方案(详情可见文档):
-
Viewport 布局
-
Vant 默认使用
px
作为样式单位,如果需要使用viewport
单位 (vw, vh, vmin, vmax),推荐使用 postcss-px-to-viewport 进行转换
-
-
Rem 布局适配
-
postcss-pxtorem 是一款 PostCSS 插件,用于将 px 单位转化为 rem 单位
-
lib-flexible 用于设置 rem 基准值
-
-
桌面端适配
-
若需要在桌面端使用 Vant,可以引入 @vant/touch-emulator,这个库会在桌面端自动将
mouse
事件转换成对应的touch
事件,使得组件能够在桌面端使用
-
最后
以上就是移动端适配的几种方式的原理了,知道了这些内容之后,其实就不难发现常用的适配插件不过是帮助我们
实现了上述的内容和一些细节,例如 自动计算、自动转换、判断机型 等等。