pc端大屏项目

1,264 阅读4分钟

  由于大部分情况下无法知道“大屏项目”部署后,用户会使用什么尺寸的屏幕,所以类比移动端,pc大屏项目也可以一样使用rem来动态设置宽高。
这里我采用 postcss-px2remlib-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()
  }
}

持续更新中~