大屏REM适配方案
分辨率
常见分辨率
- 1920 * 1080 (16:9 1080p)
- 3840 * 2160 (16:9 近4k)
- 3120 * 1080 (26:9 2k)
- 3840 * 1080 (32:9 近4k)
什么是REM?
rem在css当中充当的是length的单位,这个单位代表根元素(通常为 元素)的 font-size 大小。当用在根元素的 font-size 上面时,它代表了它的初始值。意思就是说假如html的font-size: 16px;,div的 width: 10rem; 就相当于width的长度有160px。
如何决定根元素的font-size大小
如何决定根元素的font-size大小这里就要看适配的场景(移动端 or 桌面端),以rem单位适配分为两种方式设置html的font-size:宽度、高度
我这里准备了一个demo用于观测盒子和字体之间的变化的demo
以下是根据宽和高适配后随意拉伸的实际结果(原稿分辨率1920*1080)
1920 * 1080 (16:9 1080p)
3840 * 2160 (16:9 近4k)
3120 * 1080 (26:9 2k)
3840 * 1080 (32:9 近4k)
随意拉伸
上方视频为以高度适配
上方视频为以宽度适配
各分辨率实际效果的结论
- 与UI设计原稿长宽比相同的情况下,使用高或宽几乎没有任何变化。
- 使用宽适配时,虽然左中右的宽度比例能够很好的动态变化,但是左中右两边面板两边业务组件的高度肯定不够足以显示
- 使用高适配时,左右的宽度几乎没有改变,中间的宽度被拉伸的空间比较大(中间的宽度 = 100% - 左宽度 - 右边度)
总结
通过高度的方式设置根节点 font-size 大小适配大屏效果更妥当!
考虑到大屏系统使用的显示设备通常都是宽比高更长,而且常见的分辨率也不会出现宽比高短的问题,相反移动端更适合使用宽度适配。
如何使用
I设计师出设计稿时仅需按照16:9的长宽比出设计即可。
前端人员在开发样式的时候使用rem作为单位,这里推荐两个编译器插件可以通过快捷键自动将px转换成rem:
WebStorm 可以使用 px2rem 插件
VSCode可以使用 px to rem & rpx & vw (cssrem) 插件
Echart或其他部分工具有的可以兼容rem单位,不兼容rem的可以使用下方方法 transformScaleWidth 或 transformScaleHeight ,两者的区别就是适用于宽度计算或高度计算。
核心代码
// main.js 或 主要入口文件
setRootFontSize()
window.addEventListener('resize', setRootFontSize)
window.px2rem = px2rem
window.rem2px = rem2px
// utils.js
export function setRootFontSize() {
const designHeight = 1080 // UI设计稿的高度
const designFontSize = 16 // UI设计稿的默认字体大小
const html = document.documentElement // html dom
html.style.fontSize = html.clientHeight / designHeight * designFontSize + 'px'
}
export function px2rem(num) {
let value = parseInt(num) || 1
return value/16+'rem'
}
export function rem2px(num) {
let height = document.body.clientHeight;
return (16*num*height)/1080+'px'
}
export function transformScaleWidth(px) {
return(px / 1920) * document.body.clientWidth
}
export function transformScaleHeight(px) {
return(px / 1080) * document.body.clientHeight
}
问题来了,前端依赖的ui库或者其他库里的css没办法直接修改,通常会在全局引入一个样式用来覆盖,且ui库里的样式又非常多,无法完成ui库里的rem转换。这时候可以使用 node-px2rem 工具通过 NodeJS 执行将打包后dist目录里样式文件的px转换成rem。
小心Angular
Angular组件里引入的样式文件在编译后会被编译到js文件里,例如这样:
styles:["[.content[_ngcontent-%COMP%]{padding:20px}]"]
所以这个工具没办法处理Angular组件样式里的像素单位。
针对上面这个问题的解决方案:
- 可以通过编译器插件将Angular组件引入的样式文件转换一下
- 可以在适配的时候通过node-px2rem 将src目录里全部样式文件转换一遍
node-px2rem工具的使用
// node-px2rem.js
const fs = require('fs')
const path = require('path')
const px2rem = require('node-px2rem')
const postLess = require('postcss-less') // 结合项目需要是否使用过该语法再安装
const postScss = require('postcss-scss') // 结合项目需要是否使用过该语法再安装
const postSass = require('postcss-sass') // 结合项目需要是否使用过该语法再安装
const args = process.argv
const PATH_Index = args.findIndex(item => item === '--path')
const PATH = PATH_Index >= 0 ? PATH_Index + 1 : 'dist'
const distPath = path.join(__dirname, PATH)
const cssFiles = getCssFiles(distPath)
console.log('CSS文件列表:', cssFiles)
console.log('开始处理CSS文件...')
cssFiles.forEach(file => {
const css = fs.readFileSync(file, 'utf8')
const processedCSS = px2rem.process(css, {
rootValue: 16 // 根元素字体大小
//unitPrecision: 5, // 允许 REM 单位增长到的十进制数
//propertyBlackList: [], // 不能从 px 更改为 rem 的属性
//propertyWhiteList: [], // 可以从 px 更改为 rem 的属性
//replace: false, // 替换包含 rem 的规则,而不是添加回退
//mediaQuery: false, // 允许在媒体查询中转换 px
//minPx: 1, // 如果最小 px 大于或等于 可以从 px 更改为 rem
}, {
syntax: getSyntax(file)
})
fs.writeFile(file, processedCSS, err => {
if (err) {
console.log(`文件 ${file} 处理失败!!!!!!!!!!!!!!!!!!`)
throw err
}
console.log(`文件 ${file} 处理成功.`)
})
})
console.log('CSS文件处理完毕.')
function getCssFiles(directory) {
const files = fs.readdirSync(directory)
let cssFiles = []
files.forEach(file => {
// const filePath = directory + '/' + file
const filePath = path.join(directory, file)
const stat = fs.statSync(filePath)
if (stat.isDirectory()) {
// 如果是子目录,则递归调用
cssFiles = cssFiles.concat(getCssFiles(filePath))
} else if (isStyleFile(file)) {
// 如果是CSS文件,则添加到数组中
cssFiles.push(filePath)
}
})
return cssFiles
}
// 检查是否是样式文件(css、less、scss、sass)
function isStyleFile(file) {
return /\.(css|less|scss|sass|stylus)$/.test(file)
}
// 返回postcss的语法解析器(less、scss、sass)
function getSyntax(file) {
const ext = path.extname(file)
switch (ext) {
case '.less':
return postLess
case '.scss':
return postScss
case '.sass':
return postSass
default:
return undefined
}
}
// package.json
{
...
"scripts": {
...
"build": "yarn run ng-high-memory build && node node-px2rem.js && gzipper c --include js,ts,css,html,json ./dist",
...
},
...
}