一:起步
先来了解下rem、vw、dpr分别是什么?
(1)rem 是什么
rem(font size of the root element)是指相对于根元素来做计算的字体大小单位。
例如:html { font-size: 75px }时,其他元素1rem = 75px,4rem = 300px
(2)vw 是什么
vw是基于viewport视窗的长度单位。1vw等于window.innerWidth的1%
例如:设备物理宽度为375px时,1vw = 3.75px
(3)dpr 是什么
设备像素比device pixel ratio简称dpr,即物理像素和设备独立像素的比值。
在web中,浏览器为我们提供了window.devicePixelRatio来帮助我们获取dpr。
iPhone 6、7、8的实际物理像素是750 x 1334,在开发者工具中我们可以看到:它的设备独立像素是375 x 667,设备像素比dpr为2
例如:如果给定一个元素的高度为200px(这里的px指物理像素,非CSS像素),iphone6的设备像素比dpr = 2,我们给定的height应为200px/2=100dp
二:大厂做法一 postcss-px-to-viewport
postcss-px-to-viewport的做法直接计算每个像素在设计稿中占据的%来输出vw / rem
假设:设计稿 = 375px 时
1、转换 VW 方案
'postcss-px-to-viewport': {
unitToConvert: 'px', // 需要转换的单位,默认为"px"
viewportWidth: 375, // 视窗的宽度,对应设计稿的宽度
viewportUnit: 'vw', // 指定需要转
fontViewportUnit: 'vw', // 字体使用的视口单位
unitPrecision: 13 // 指定`px`转换为视窗单位值的小数后 x位数
...
}
算出1px 在设计稿中的占比,再换算成 vw
1px = 1 / 375 = 0.2666666666666% 即 100px = 26.6666666666666% = 26.6666666666666vw
// 实际渲染时(375px 的屏幕)
26.6666666666666vw = 26.6666666666% * 375 = 100px
在 转换成vw的方案设置媒体查询超出宽度范围后固定body宽度,内容居中时,会出现样式过大影响查看的问题。
@media screen and (min-width: 768px) {
html {
max-width: 768px;
}
}
2、转换 REM 方案
为了避免不同浏览器的默认字体大小不一样的问题,我们需要固定好root元素 html的font-size
利用postcss-px-viewport不支持内联样式的转换。来设置root元素 html的内联font-size: 16px;来固定root的字体大小以适配转换成rem的方案。
1px = 1 / 375 = 0.2666666666666% 即 100px = 26.6666666666666% = 26.6666666666666rem
因为我们设置了root元素 html的内联font-size: 16px;来固定root的字体大小。所以实际渲染时(375px 的屏幕),容器26.6666666666666rem = 26.6666666666666 * 16 = 426.6666666666656px
需要更改viewportWidth的大小来和设计图适配。
设计图与viewportWidth的倍数关系 = 426.6666666666656 / 100 = 4.26656倍。设置viewportWidth: 1599.96 (375 * 4.26656 = 1599.96)
三:大厂做法二 rem + vw
同样假设:设计稿 = 375px 时
1vw = 3.75px
1px = 0.2666666666666667vw
100px = 26.6666666666666667vw
1rem = 26.6666666666666667vw = 100px
向上兼容自适应的时候,设置好@media对应不同的font-size即可
同样设置媒体查询超出宽度范围后固定body宽度,内容居中。
html {
font-size: 26.6666666666666667vw;
margin: 0 auto;
body {
font-size: 0.14rem;
}
}
@media screen and (min-width: 768px) {
html {
font-size: 9vw;
max-width: 768px;
}
}
四:lib-flexible方案
根据设备屏幕的DPR,自动设置最合适的高清缩放(body的字体大小,重置根目录的字体大小)。width=设备独立像素的宽度,初始/最大/最小缩放=1。通过 js计算root fontSize的值,使1rem = 设计稿 / 10。
配置postcss-plugin-px2rem
module.exports = {
css: {
loaderOptions: {
postcss: {
plugins: [
require('postcss-plugin-px2rem')({
rootValue: 192, // 换算基数,默认为100
exclude: /(node_module)/, // 默认false, 可以用正则表达式排除某些文件的转换
mediaQuery: false, // 允许在媒体查询中转换px
})
]
}
}
}
}
其中rootValue的计算逻辑是这样的,如果是按照640分辨率来开发页面,那么rootValue的值为640 / 10 = 64,如果是按照1920来开发,那么rootValue为:1920 / 10 = 192。这里除以10是因为lib-flexible设定比例 1em = 10px
修改lib-flexible源码,以增加对大屏的适配
打开node_module中lib-flexible文件的flexible.js。其中定义了一个名为refreshRem的方法
function refreshRem() {
var width = docEl.getBoundingClientRect().width;
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
可以看到这个方法中有一个width / dpr > 540的判断。猜也能猜出来这个540是一个极限值。那么,如果要设置1920分辨率的屏幕只需要把这个540修改了即可,修改后的方法:
function refreshRem() {
var width = docEl.getBoundingClientRect().width;
// 当屏幕超过1920px以后就不在随着屏幕的变大而变大了
if (width / dpr > 1920) {
width = 1920 * dpr;
}
// 当屏幕小于375px以后就不再随着屏幕的变小而变小了
if (width / dpr < 375) {
width = 375 * dpr;
}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
lib-flexible是一个过渡方案,存在一定的问题,建议大家开始使用viewport来替代此方案
lib-flexiblet源码
五:总结 - 选什么方案?
1、如果是只做移动端
选择postcss-px-to-viewport – vw方案
2、小屏向上兼容大屏
当需要从移动端适配到平板、PC屏幕
(1)选择postcss-px-to-viewport – rem最方便了
优点
自动转换 UI框架中的单位。
配合media媒体查询设置root fontSize适配不同分辨率的大小以及限制最大宽度。
缺点
所有设置转换的单位都会被转换掉,如果需要某些样式固定大小可以使用大写PX来规避转换。
(2)选择rem + vw 方案最灵活
优点
配合media媒体查询设置root fontSize适配不同分辨率的大小以及限制最大宽度。
高度自定义,需要转换的地方 转换成rem。
缺点
当需要把 UI框架中的单位也转换时,会非常的头大。需要一个一个覆盖。
(3)postcss-px-to-viewport – vw 方案不适合
该方案在限制最大宽度的时候,由于大小都是根据 viewport来决定的。所以限制了最大宽度时里面的内容依旧会随viewport变大而变大。故不合适
3、大屏向下兼容小屏
建议选择postcss-px-to-viewport – rem 方案
优点
自动转换 UI框架中的单位,省事。
设置最小宽度居中,超出部分滚动条。
适配比设计稿更大的屏幕时把root fontSize设置为更大即可。
缺点
所有设置转换的单位都会被转换掉
(2)选择 rem + vw方案(较一般)
假设屏幕 1024px
1vw = 10.24px
1px = 0.09765625vw
100px = 9.765625vw
1rem = 9.765625vw = 100px
优点
设置时需要把1rem设置成100px 对应的 vw值的值(防止小于浏览器最小字体),编写时根据设计图px / 100来编写。
缺点
需要写多个媒体查询更改root fontSize(因为存在字体太大导致一屏内容显示太少问题)
当需要把 UI框架中的单位也转换时,会非常的头大。需要一个一个覆盖。
无法设置最小宽度居中内容。
(3)postcss-px-to-viewport – vw(不合适)
由于国产浏览器中的root fontSize小于默认最小字体(一般是 12px)时,会强制保持root fontSize = 12px ,因此该方案不合适。