rem方案
rem
是相对于html
节点的font-size
来做计算的一个相对单位,在运行时
动态修改html
节点的font-size
大小,从而影响所有使用rem
单位的布局。
1rem该设为多少?其实没有什么统一的标准。国内主流网站也都在使用rem
方案,具体方案和阿里的大同小异,根据是否缩放viewport
分为两个阵营:
不缩放viewport(屏幕宽度为375px
):
- 马蜂窝 1rem = 37.5px
- 小米 1rem = 52.0833px
- 小红书 1rem = 50px
缩放viewport(屏幕宽度为375px
,viewport scale为0.5
): - B站 1rem = 46.875px
- 搜狐 1rem = 75px
最早的rem方案是阿里早期提出并在手机淘宝上使用,同期封装开源了lib-flexible适配库,下面以flexible
的方案为例介绍:
首先约定将设备的布局视窗
进行10
等分,将html
节点的font-size
设置为页面布局视窗
的1/10
,即1rem
就等于页面布局视窗
的1/10
,这就意味着我们后面使用的rem
都是按照页面
比例来计算的。
以iPhone6
为例:布局视窗为375px
,则1rem = 37.5px
,这时UI给定一个元素的宽为75px
,我们只需要将它设置为75 / 37.5 = 2rem
。
接下来要做的就是,根据设备的独立像素比
,在运行时
控制根节点font-size
的大小,使用css或者js都可以实现:
一种是媒体查询,优点:不需要额外使用js
去更改html
的字体,缺点:不连续,或者说并能完全实现对所有设备的布局规范统一;
html {
font-size: 50px
}
@media only screen and (min-device-width: 375px) and (-webkit-min-device-pixel-ratio: 2) {
html {
font-size: 37.5px
}
}
@media only screen and (min-device-width: 360px) and (-webkit-min-device-pixel-ratio: 3) {
html {
font-size: 36px
}
}
另一种是js
动态更改html
字体,优点:连续;缺点:不如直接写媒体查询的体验好;
// set 1rem = viewWidth / 10
function setRemUnit () {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
window.onload = function(){
setRemUnit()
};
window.onresize = function(){
setRemUnit()
};
该方案是阿里手淘早期使用,并封装开源了lib-flexible库,它的主要思想有三点:
- 根据
dpr
的值来修改viewport
实现1px的线 - 根据
dpr
的值来修改html
的font-size
,从而使用rem
实现等比缩放 - 使用
Hack
手段用rem
模拟vw
特性
存在的问题: - 需要额外
内联引入
一个lib-flexible库,保证在所有资源加载之前执行这个JS,使用iframe引用就会出现问题。 html
的font-size
设置到12px以下还是会按照12px=1rem来计算,这样所有使用了rem单位的尺寸都是错的。- 在奇葩的
dpr
设备上表现效果不太好,比如一些华为的高端机型 用rem布局会出现错乱。
vw方案
1. 原理
vw是相对单位,1vw表示屏幕宽度的1%。基于此,我们可以把所有需要适配屏幕大小等比缩放的元素都使用vw做为单位。不需要缩放的元素使用px做单位。
举个例子。设计师交付的设计稿宽度是750px,设计稿上一个标题的fontSize标注尺寸是32px。(32/750)*100% = 4.27% ,换句话说这个标题的字号占屏幕宽度的占比是4.27%,不管任何屏幕都是如此。4.27% 即 4.27vw。
对于任何需要等比缩放的元素,在写CSS设置样式时直接换算成vw即可,尺寸 = 100vw*设计稿标注大小/设计稿宽度。
2. 适配代码
假设设计稿尺寸是750px,页面有一个按钮,按钮文字标注大小28px,按钮高度标注为48px,宽度为120px,边框为1px不缩放。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.button {
width: 16vw;/* 100vw*120/750 */
font-size: 3.73vw;/* 100vw*28/750 */
line-height: 6.4vw;/* 100vw*48/750 */
border: 1px solid #000;/*不需要缩放的部分用px*/
text-align: center;
}
</style>
</head>
<body>
<div class="button">按钮</div>
</body>
</html>
在写样式时会发现,虽然不用写JS做适配,但标注尺寸换算为vw又麻烦又不直观。
我们可以在CSS里使用calc来换算换,只不过需要注意新语法的兼容性。
:root {
--ratio: calc(100vw/750);
}
.button {
font-size: calc(100vw*28/750);/* 可以直接用calc */
line-height: calc(100vw*48/750);
width: calc(120*var(--ratio));/* 也可以用calc配合var使用,IE不支持var */
border: 1px solid #000;/*不需要缩放的部分用px*/
text-align: center;
}
在正式的项目里,我们也可以用SCSS,把换算交给预处理器
@function px2vw($px) {
@return $px * 100vw / 750;
}
.button {
width: px2vw(120);
font-size: px2vw(28);
line-height: px2vw(48);
border: 1px solid #000;
text-align: center;
}
3. 借助 postcss-px-to-viewport,
将 px 自动转为 vw
1. 安装 postcss-px-to-viewport:yarm add postcss-px-to-viewport -D
2. 在项目根目录下添加postcss.config.js文件
module.exports = {
plugins: [
// to edit target browsers: use "browserslist" field in package.json
require('autoprefixer'),
require("postcss-px-to-viewport")({
unitToConvert: "px", // 要转化的单位
viewportWidth: 375, // UI设计稿的宽度
unitPrecision: 6, // 转换后的精度,即小数点位数
propList: ["*"], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
viewportUnit: "vw", // 指定需要转换成的视窗单位,默认vw
fontViewportUnit: "vw", // 指定字体需要转换成的视窗单位,默认vw
selectorBlackList: ["wrap"], // 指定不转换为视窗单位的类名,
minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
mediaQuery: true, // 是否在媒体查询的css代码中也进行转换,默认false
replace: true, // 是否转换后直接更换属性值
exclude: [/node_modules/], // 设置忽略文件,用正则做目录名匹配
landscape: false // 是否处理横屏情况
})
]
}
3. 然后重新运行项目,使配置文件生效
适配方案对比
- 动态REM方案
-
- 适配原理稍复杂
- 需要使用JS
- 设计稿标注的px换算到CSS的rem计算简单
- 方案灵活技能实现整体缩放又能实现局部不缩放
- vw方案
-
- 适配原理简单
- 不需要JS即可适配
- 设计稿标注的px换算到CSS的vw计算复杂
- 方案灵活技能实现整体缩放又能实现局部不缩放