由于大部分情况下无法知道“大屏项目”部署后,用户会使用什么尺寸的屏幕,所以类比移动端,pc大屏项目也可以一样使用rem来动态设置宽高。
这里我采用 postcss-px2rem 和 lib-flexible 来实现px转化rem。
有看到网上挺多人推荐用 px2rem-loader ,但发现这些人都是用的vue-cli2的脚手架。
自动布局和宽度自适应
1、安装依赖包
npm i postcss-px2rem -D
npm i lib-flexible -S
2、配置vue.config.js文件
这里的作用是将px换算成rem
3、动态计算根元素的font-size
我们知道,rem是根据根元素的font-size大小来确定当前元素各个单位大小的。所以我们还需要动态计算根元素的font-size。
在main.js中引入下列代码:
import 'lib-flexible/flexible.js';
我这里设置的元素宽度是960px,但是没有占据页面的一半,我1920的宽度,计算出来的font-size却只有54px,这显然是不合理的。是不是插件出了什么问题呢?
我们打开引入的这个文件,进去瞅瞅看。
查看 node_modules 中的 lib-flexible/flexible.js 文件
我们把代码改一下:(修改源码后会造成无法使用jenkins打包、使用git进行代码管理时别人也要进行修改)
function refreshRem(){
var width = docEl.getBoundingClientRect().width;
if (width / dpr > 540) {
width = width * dpr; // 当屏幕宽度超过540的时候我们应该以实际宽度重新计算,从而来做到适应PC
}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
最后我们运行一下
一切ok~。
我的浏览器宽度是1920px,可以看到html标签的font-size被设置成了192px。
这个div标签我给的960px宽度,可以看到它被转换成了5rem。
注:像是一些不需要转换的宽度,如border的宽度,可以采用如下代码:
border: 1px solid red; /*no*/
配合vw、vh使用实现高度自适应
上面的方式实际上只解决了宽度自适应,对于大屏项目而言,你无法确定用户实际使用的屏幕宽高比,所以整体布局上面的高度自适应,我这里使用了vh。
根据模块的高度相对于整个页面高度的占比,进行百分比计算。
例如:我的设计图整体是 1080px,分为Header部分和Content部分上下结构布局。其中Header部分为130px,大概占 130/1080 约等于 0.12,所以就将Header设置
height: 12vh;,将Content设置height: 88vh;
自适应高度解决方案
上述的 flexible 只能使宽度自适应,无法让高度自适应。在实际开发过程中,我这边是使用了 vh 来配合开发。
最开始的想法是将相应的 px 值转换成 vh ,来直接使用,代码如下(这里使用的是scss、设计图的高度为1080px):
$px8: 0.74vh; // 8px -> 0.74vh
$px10: 0.92vh;
$px12: 1.11vh; // 12px -> 1.11vh
$px14: 1.3vh; // 14px -> 1.3vh
$px15: 1.39vh; // 15px -> 1.39vh
$px16: 1.48vh; // 16px -> 1.48vh
$px18: 1.67vh; // 18px -> 1.67vh
$px20: 1.85vh; // 20px -> 1.85vh
$px24: 2.22vh;
$px27: 2.5vh;
$px30: 2.78vh; // 30px -> 2.78vh
$px42: 3.89vh;
使用方式:
h2{
height: $px20; // 高度为20px
}
会发现这样写的话很累,每多一个px值,就得相应的计算后添加一行。
查了一下资料,没有找到循环生成变量的方法。
思考一番之后,觉得即使有循环生成变量的方法,最多也只是循环生成 1~100的变量(因为这里是全局的css,过多会造成内存浪费,而且比较大的值也一般用不到),对于一些超出100的布局还是得手动计算,也不是很方便。
最后决定使用sass函数来解决它~
@mixin h($height) {
height: ($height*100/1080)+vh;
}
@mixin maxh($height) {
max-height: ($height*100/1080)+vh;
}
@mixin w($height) {
width: ($height*100/1080)+vh;
}
@mixin wCalc($rate, $height) {
width: calc($rate + ($height*100/1920));
}
@mixin lh($height) {
line-height: ($height*100/1080)+vh;
}
@mixin margin($param) {
margin: ($param*100/1080)+vh;
}
@mixin margin2($param1, $param2) {
margin: ($param1*100/1080)+vh ($param2*100/1080)+vh;
}
@mixin margin3($param1, $param2, $param3) {
margin: ($param1*100/1080)+vh ($param2*100/1080)+vh ($param3*100/1080)+vh;
}
@mixin margin4($param1, $param2, $param3, $param4) {
margin: ($param1*100/1080)+vh ($param2*100/1080)+vh ($param3*100/1080)+vh ($param4*100/1080)+vh;
}
@mixin mt($height) {
margin-top: ($height*100/1080)+vh;
}
@mixin mb($height) {
margin-bottom: ($height*100/1080)+vh;
}
@mixin mr($height) {
margin-right: ($height*100/1080)+vh;
}
@mixin ml($height) {
margin-left: ($height*100/1080)+vh;
}
@mixin padding($param) {
padding: ($param*100/1080)+vh;
}
@mixin padding2($param1, $param2) {
padding: ($param1*100/1080)+vh ($param2*100/1080)+vh;
}
@mixin padding3($param1, $param2, $param3) {
padding: ($param1*100/1080)+vh ($param2*100/1080)+vh ($param3*100/1080)+vh;
}
@mixin padding4($param1, $param2, $param3, $param4) {
padding: ($param1*100/1080)+vh ($param2*100/1080)+vh ($param3*100/1080)+vh ($param4*100/1080)+vh;
}
@mixin pt($height) {
padding-top: ($height*100/1080)+vh;
}
@mixin pb($height) {
padding-bottom: ($height*100/1080)+vh;
}
@mixin pl($height) {
padding-left: ($height*100/1080)+vh;
}
@mixin pr($height) {
padding-right: ($height*100/1080)+vh;
}
@mixin fs($height) {
font-size:($height*100/1080)+vh;
}
@mixin lh($height) {
line-height:($height*100/1080)+vh;
}
@mixin borderraduis($height) {
border-radius:($height*100/1080)+vh;
}
@mixin borderraduis4($param1, $param2, $param3, $param4) {
border-radius:($param1*100/1080)+vh ($param2*100/1080)+vh ($param3*100/1080)+vh ($param4*100/1080)+vh;
}
@mixin top($height) {
top:($height*100/1080)+vh;
}
@mixin left($height) {
left:($height*100/1080)+vh;
}
@mixin right($height) {
right:($height*100/1080)+vh;
}
@mixin bottom($height) {
bottom:($height*100/1080)+vh;
}
@mixin translateY($height) {
transform: translateY(($height*100/1080)+vh);
}
@mixin translateX($height) {
transform: translateX(($height*100/1920)+vw);
}
@mixin borderTop($height, $color) {
border-top: ($height*100/1080)+vh solid $color;
}
@mixin borderBottom($height, $color) {
border-bottom: ($height*100/1080)+vh solid $color;
}
@mixin borderRightVh($height, $color) {
border-right: ($height*100/1080)+vh solid $color;
}
@mixin borderLeftVh($height, $color) {
border-left: ($height*100/1080)+vh solid $color;
}
@mixin borderRight($height, $color) {
border-right: ($height*100/1920)+vw solid $color;
}
@mixin borderLeft($height, $color) {
border-left: ($height*100/1920)+vw solid $color;
}
可以看到,这里把对高度有影响的样式全都拿出来做了计算,不需要担心内存使用过多的问题,因为这里是sass的函数,最终会被编译成相应的样式。
使用方法:
h2{
@include fs(18);
@include lh(42);
@include margin3(5, 10, 10);
}
这样就基本可以做到自适应了~
修正 echart 中的内容大小
做大屏项目,一般都会有很多图表,我这边使用的是 echart。
echart配置文件中,设置相应的fontSize或者宽高边距,一般都是一个数字,这个数字默认的单位是px。这就对我们的项目产生影响了,因为这个数字是设计图上相对于1080px高度的数字。当屏幕大小发生变化时,我们需要这个数字也动态的改变。
接下来,我们对其进行一定得处理。
在 util.js 文件中
// 获取窗口可视区高度
const getClientHeight = () => {
let clientHeight = 0;
if(document.body.clientHeight && document.documentElement.clientHeight){
clientHeight = (document.body.clientHeight<document.documentElement.clientHeight)?document.body.clientHeight:document.documentElement.clientHeight;
}
else{
clientHeight = (document.body.clientHeight>document.documentElement.clientHeight)?document.body.clientHeight:document.documentElement.clientHeight;
}
return clientHeight;
}
// 获取当前窗口可视区/1080的比例
export const getHeightRate = () => {
const height = getClientHeight();
return height / 1080;
}
现在我们就可以拿到当前窗口可视区相对于设计图(1080px)的占比了。
接下来就可以处理echart配置文件啦~
使用:
import { getHeightRate } from '@/common/js/util.js'
legend: {
data: charts.names,
textStyle: {
fontSize: getHeightRate() * 12,
color: '#fff'
},
bottom: '4%',
icon: 'line',
itemWidth: getHeightRate() * 10,
itemHeight: getHeightRate() * 10
},
可以看到,我这里的例子是对 legend 中的内容进行了处理:
1、修正fontSize的大小
2、修正icon图标的高度和宽度
图片动效的问题
由于实际使用的屏幕宽度未知,需要做自适应的情况下,是没办法使用雪碧图的。这里考虑把动图的全部(每一帧)图片都引入项目,使用img标签做自适应处理。
<img class="header-decorate-img" v-for="(item, index) in decorateImgList" v-show="decorateImgIndex===index" :key="index" :src="item" alt="">
export default {
data() {
return {
decorateImgList: [], // 所有图片的引入
decorateImgIndex: 0, // 头部当前的显示图片index,循环这个index
decorateTimer: null
}
},
methods: {
// 动画列表处理
headerAnimateListHandler() {
const picNum = 51; // 我这里的动图,由51张帧图片组成
this.decorateImgList = [];
// 这里通过require引入所有的图片
for(let i=0; i<picNum; i++) {
// 这里需要注意图片的名称
this.decorateImgList.push(require(`@/assets/image/header-decorate-animate/decorate_${i}.png`))
}
},
// 开启头部动画
headerAnimate(){
let i = 0;
this.decorateTimer = setInterval(() => {
this.decorateImgIndex = i;
i++;
// 图片是 0~50,一共51张图
if(i > 50) {
// 从头开始动画
i = 0;
}
}, 40);
}
},
created() {
this.headerAnimateListHandler();
this.headerAnimate()
}
}
持续更新中~