rem方案
媒体查询+rem
原理: 动态根据当前屏幕的宽度,自动改变页面中html font-size的大小, 能让所有使用rem单位的元素跟随着发生变化,而使用px单位的元素不受影响。
1、如何根据屏幕尺寸计算html font-size的大小?
目前rem布局方案中,将网页等分成10份, HTML标签的字号为视口宽度的 1/10
// 在index.html的head标签底部嵌入以下script脚本
<script>
// 根据屏幕的宽度, 动态设置根元素的fontSize
const setRootFontSize = () => {
// 获取屏幕宽度
var width = document.documentElement.clientWidth;
//设置html的fontSize
document.documentElement.style.fontSize = (width / 10) + 'px'
}
window.onresize = setRootFontSize;
setRootFontSize();
</script>
// 所以 1rem = html fontSize = 屏幕宽度的十分之一
注意:10是随意设置的(将屏幕划分为10等份),为了方便计算;这个除数不宜设置太大,因为大部分浏览器的最小字体为12px。
2、若设计图宽度为375px(iPhone SE),上有一个宽度为160px,高为80px的
/* 对于某个width height 300px的元素来说 css文件中需要手动转换成rem */
div {
width: calc(160rem / 37.5);
height: calc(80rem / 37.5);
}
可见上述方式,在css文件中需要进行大量的计算,十分不便
3、优化方案,引入.scss文件, 使用@function的功能来计算rem的值。 rem * html fontSize = 元素的实际px
// style/theme.scss文件中
// 设计稿屏幕的宽度
$design-width: 750;
@function px2rem($px) {
// 计算出是几个rem
@return $px / ($design-width / 10) + rem;
}
// demo.scss文件中使用
@import '../style/theme.scss';
.wrapper {
width: px2rem(300);
height: px2rem(300);
box-sizing: border-box;
background-color: pink;
border: 1px solid #ccc;
}
.el {
font-size: 12px;
}
优缺点:
-
优点: 动态Rem方案既能实现页面级整体缩放,又能个性化控制某些元素不缩放
-
缺点: 和根元素font-size值强耦合,系统字体放大或缩小时,会导致布局错乱;适配原理稍复杂,html文件头部需插入一段js代码
flexible 适配(过渡方案)
使用flexible js配合rem实现在不同宽度的设备中,网页元素尺寸等比缩放效果
- flexible.js是手淘开发出的一个用来适配移动端的js框架。
- 核心原理就是根据不同的视口宽度给网页中html根节点设置不同的font-size
1、使用customize-cra修改react的webpack配置,安装依赖更改package.json里边的配置
npm install customize-cra react-app-rewired --dev
// 更改package.json中的script设置
//原来的:
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
//修改后:
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
px2rem适配的配置:
npm install postcss-px2rem lib-flexible --save
npm install react-app-rewire-postcss --save-dev
cnpm i postcss-px2rem-exclude -D
2、在项目根目录创建config-overrides.js
const {override} = require("customize-cra")
const path = require("path")
const rewirePostcss = require('react-app-rewire-postcss');
const px2rem = require('postcss-px2rem')
module.exports = override(
(config,env)=>{
// 重写postcss
rewirePostcss(config,{
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
require("postcss-px2rem-exclude")({
// remUnit 等于 设计稿宽度 750除以10 75
remUnit: 75,
exclude: /node_modules/i
}),
],
});
return config
},
);
3、index.js中引入
import 'lib-flexible'
4、在 index.html 中
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
5、flexible的优缺点:
- 由于其缩放的缘故,video 标签的视频播放器的样式在不同 dpr 的设备上展示差异很大;
- lib-flexible 对安卓手机的特殊处理,即:一律按 dpr = 1 处理;
- 不再兼容 @media 的响应式布局,因为 @media 语法中涉及到的尺寸查询语句,查询的尺寸依据是当前设备的物理像素,和 flexible 的布局理论(即针对不同 dpr 设备等比缩放视口的 scale 值,从而同时改变布局视口和视觉视口大小)相悖,因此响应式布局在“等比缩放视口大小”的情境下是无法正常工作的;
- 引用lib-flexible github作者的话:flexible
vw/vh 适配方案
vw 作为布局单位,从底层根本上解决了不同尺寸屏幕的适配问题,因为每个屏幕的百分比是固定的、可预测、可控制的。 viewport 相关概念如下:
- vw:是 viewport's width 的简写,1vw 等于 window.innerWidth 的 1%;
- vh:和 vw 类似,是 viewport's height 的简写,1vh 等于 window.innerHeihgt 的 1%;
- vmin:vmin 的值是当前 vw 和 vh 中较小的值;
- vmax:vmax 的值是当前 vw 和 vh 中较大的值;
1、设置meta标签:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
2、插件px 自动转换为 vw
npm install postcss-px-to-viewport --save-dev
3、webpack配置:vue cli2.0
// .postcssrc.js
module.exports = {
plugins: {
// ...
'postcss-px-to-viewport': {
unitToConvert: 'px', // 需要转换的单位,默认为"px"
viewportWidth: 750, // 设计稿的视窗宽度
unitPrecision: 5, // 单位转换后保留的精度
propList: ['*', '!font-size'], // 能转化为 vw 的属性列表 即font-size不进行转换vw
viewportUnit: 'vw', // 希望使用的视窗单位
fontViewportUnit: 'vw', // 字体使用的视窗单位
selectorBlackList: [], // 需要忽略的 CSS 选择器,不会转为视窗单位,使用原有的 px 等单位
minPixelValue: 1, // 设置最小的转换数值,如果为 1 的话,只有大于 1 的值会被转换
mediaQuery: false, // 媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
exclude: [/node_modules/], // 设置忽略文件,用正则做目录名匹配 默认值undefined
include: //src/mobile//, // 如果设置了include,那将只有匹配到的文件才会被转换, 只转换 'src/mobile' 下的文件
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件
landscapeUnit: 'vw', // 横屏时使用的单位
landscapeWidth: 1125, // 横屏时使用的视窗宽度
},
},
};
4、标注不需要转换的属性
在项目中,如果设计师要求某一场景不做自适配,需为固定的宽高或大小,这时我们就需要利用 postcss-px-to-viewport 插件的 Ignoring 特性,对不需要转换的 css 属性进行标注,示例如下所示:
- /* px-to-viewport-ignore-next */ —> 下一行不进行转换.
- /* px-to-viewport-ignore */ —> 当前行不进行转换
/* example input: */
.class {
/* px-to-viewport-ignore-next */
width: 10px;
padding: 10px;
height: 10px; /* px-to-viewport-ignore */
border: solid 2px #000; /* px-to-viewport-ignore */
}
/* example output: */
.class {
width: 10px;
padding: 3.125vw;
height: 10px;
border: solid 2px #000;
}
5、react中使用 postcss-px-to-viewport 转换px
5、1 安装:postcss-px-to-viewport 和 postcss-loader
npm install postcss-loader postcss-px-to-viewport --save-dev
5、2 暴露webpack.config.js
npm run eject
5、3 inject导出 webpack.config.js(操作不可逆 慎用)
loader: require.resolve('postcss-loader'),
options: {
postcssOptions: {
// Necessary for external CSS imports to work
// https://github.com/facebook/create-react-app/issues/2677
ident: 'postcss',
config: false,
plugins: !useTailwind
? [
'postcss-flexbugs-fixes',
[
'postcss-preset-env',
{
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
},
],
// Adds PostCSS Normalize as the reset css with default options,
// so that it honors browserslist config in package.json
// which in turn let's users customize the target behavior as per their needs.
// 新增
[
'postcss-px-to-viewport',
{
unitToConvert: 'px', // 需要转换的单位,默认为"px"
viewportWidth: 750, // 设计稿的视窗宽度
unitPrecision: 5, // 单位转换后保留的精度
propList: ['*', '!font-size'], // 能转化为 vw 的属性列表 即font-size不进行转换vw
viewportUnit: 'vw', // 希望使用的视窗单位
fontViewportUnit: 'vw', // 字体使用的视窗单位
selectorBlackList: ['vant'], // 需要忽略的 CSS 选择器,不会转为视窗单位,使用原有的 px 等单位
minPixelValue: 1, // 设置最小的转换数值,如果为 1 的话,只有大于 1 的值会被转换
mediaQuery: false, // 媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
exclude: [/node_modules/], // 设置忽略文件,用正则做目录名匹配 默认值undefined
include: //src//, // 如果设置了include,那将只有匹配到的文件才会被转换, 只转换 'src/mobile' 下的文件
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件
landscapeUnit: 'vw', // 横屏时使用的单位
landscapeWidth: 1125, // 横屏时使用的视窗宽度
}
],
'postcss-normalize',
]
: [
'tailwindcss',
'postcss-flexbugs-fixes',
[
'postcss-preset-env',
{
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
},
],
],
},
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
},
5、4 重写webpack.config.js
// 项目根目录创建config-overrides.js
const {override} = require("customize-cra")
const path = require("path")
const rewirePostcss = require('react-app-rewire-postcss');
module.exports = override(
(config,env)=>{
// 重写postcss
rewirePostcss(config,{
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
require('postcss-px-to-viewport')({
unitToConvert: 'px', // 需要转换的单位,默认为"px"
viewportWidth: 750, // 设计稿的视窗宽度
unitPrecision: 5, // 单位转换后保留的精度
propList: ['*', '!font-size'], // 能转化为 vw 的属性列表 即font-size不进行转换vw
viewportUnit: 'vw', // 希望使用的视窗单位
fontViewportUnit: 'vw', // 字体使用的视窗单位
selectorBlackList: [], // 需要忽略的 CSS 选择器,不会转为视窗单位,使用原有的 px 等单位
minPixelValue: 1, // 设置最小的转换数值,如果为 1 的话,只有大于 1 的值会被转换
mediaQuery: false, // 媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
exclude: [/node_modules/], // 设置忽略文件,用正则做目录名匹配 默认值undefined
include: //src/mobile//, // 如果设置了include,那将只有匹配到的文件才会被转换, 只转换 'src/mobile' 下的文件
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件
landscapeUnit: 'vw', // 横屏时使用的单位
landscapeWidth: 1125, // 横屏时使用的视窗宽度
}),
],
});
return config
},
);
vw适配方案(手动计算)
不需要javascript的适配方案
原理: vw是相对单位,vw = viewportWidth 视口宽度, 1vw = 屏幕视口宽度的1%;可以将需要适配屏幕大小等比例缩放的元素都使用vw作为单位,不需要缩放的元素使用px作为单位
计算公式: 100vw * 设计稿标注大小 / 设计稿的宽度
/* 可以在CSS里使用calc来换算换,只不过需要注意新语法的兼容性。*/
:root {
--ratio: calc(100vw/750);
}
.button {
font-size: calc(100vw*28/750); /* 直接使用calc */
width: calc(120*var(--ratio)); /* 也可以用calc配合var使用, IE不支持var */
}
/* 正式项目中,使用scss 将换算公式交给预处理器 */
// style/theme文件中
$aide-color: #999;
@function px2vw($px) {
@return $px * 100vw / 750;
}
/* 使用时 */
@import '../../../style/theme';
.button {
width: px2vw(120);
font-size: px2vw(28); /* 直接写设计稿标注的尺寸 */
border: 1px solid #ccc;
}
优缺点:
- 优点: 适配原理简单;不需要JS即可适配;方案灵活既能实现整体缩放又能局部不缩放。
- 缺点:虽然不用写JS做适配,但标注尺寸px换算为css的vw计算复杂, 使用viewport 适配方案
- 总结: vw的兼容性比rem稍微差一些;ios8、安卓4.4及以上才完全支持。这也是为什么之前rem布局一直更流行的原因。
以上两种方案,都只是在不同设备上的等比缩放;并不是响应式布局(响应式布局还需配合媒体查询)