可视化大屏自适应方案

前言

最近在做可视化大屏项目,遇到自适应的问题,这边做下记录。

方案

方案一:利用css的transform属性构造组件以及设计百分比以及scal()方法:

第一步:先写一个组件,width = 100vw,height = 100%,作为大屏展示的背景

<template>
  <div class="screen-large">
    <div class="screen-large_wrap">
      <slot />
    </div>
  </div>
</template>

<style lang="scss">
  .screen-large {
    width: 100vw;
    min-height: 100%;
    max-height: 100vh;
    overflow: hidden;
    background: #567789;
  }
</style>

第二步:根据设计同学提供的设计图可以计算出每部分区域的百分比,例如总尺寸是w*h,其中一个图标宽高是w1 * h1,实现常规切图,此时由1–>2可得:

<template>
  <div class="screen-large">
    <div class="screen-large_wrap" :style="style">
      <slot />
    </div>
  </div>
</template>

<script>
  export default {
    name: 'Screen',
    data() {
      return {
        style: {
          width: `${this.w}px`,
          height: `${this.h}px`,
          transform: 'scale(1) translate(-50%, -50%)', // 默认不缩放,垂直水平居中
        },
      };
    },
    props: {
      w: { // 设计图尺寸宽
        type: Number,
        default: 1920,
      },
      h: { // 设计图尺寸高
        type: Number,
        default: 1080,
      },
    },

  };
</script>

<style lang="scss">
  .screen-large {
    width: 100vw;
    min-height: 100%;
    max-height: 100vh;
    overflow: hidden;
    background: #567789;

    .screen-large_wrap {
      transform-origin: 0 0;
      position: absolute;
      top: 50%;
      left: 50%;
      padding: 18px 64px;
    }
  }
</style>

第三步:基于第二步,需要根据大屏具体尺寸计算缩放比例,以及设置缩放比例,需要注意的是,绑定resize事件一定别忘了防抖,页面销毁别忘了移除监听事件:

<template>
  <div class="screen-large">
    <div class="screen-large_wrap" :style="style">
      <slot />
    </div>
  </div>
</template>

<script>
  export default {
    name: 'Screen',
    data() {
      return {
        style: {
          width: `${this.w}px`,
          height: `${this.h}px`,
          transform: 'scale(1) translate(-50%, -50%)', // 默认不缩放,垂直水平居中
        },
      };
    },
    props: {
      w: { // 设计图尺寸宽
        type: Number,
        default: 1600,
      },
      h: { // 设计图尺寸高
        type: Number,
        default: 1200,
      },
    },
    mounted() {
      this.setScale();
      this.onresize = this.debounce(() => this.setScale(), 100);
      window.addEventListener('resize', this.onresize);
    },
    methods: {
      // 防抖
      debounce(fn, t) {
        const delay = t || 500;
        let timer;
        // eslint-disable-next-line func-names
        return function () {
          // eslint-disable-next-line prefer-rest-params
          const args = arguments;
          if (timer) {
            clearTimeout(timer);
          }
          const context = this;
          timer = setTimeout(() => {
            timer = null;
            fn.apply(context, args);
          }, delay);
        };
      },
      // 获取缩放比例
      getScale() {
        const w = window.innerWidth / this.w;
        const h = window.innerHeight / this.h;
        return w < h ? w : h;
      },
      // 设置缩放比例
      setScale() {
        this.style.transform = `scale(${this.getScale()}) translate(-50%, -50%)`;
      },
    },
    beforeDestroy() {
      window.removeEventListener('resize', this.onresize);
    },
  };
</script>

<style lang="scss">
  .screen-large {
    width: 100vw;
    min-height: 100%;
    max-height: 100vh;
    overflow: hidden;
    background: #567789;

    .screen-large_wrap {
      transform-origin: 0 0;
      position: absolute;
      top: 50%;
      left: 50%;
      padding: 18px 64px;
    }
  }
</style>

引用方式和最终呈现效果


非常规尺寸可能会更具长宽比导致左右或者上下留出空白,解决方法有两种:

  1. 采用@media媒介查询解决

  2. 如果是一般比例分辨率的屏幕:计算补齐白边所需的px。

    通过浏览器的宽高比与设计稿的宽高比计算出留白的位置(宽度或是高度方向)。

    先在可能会出现留白的宽度或高度方向上进行缩放,然后统一进行scale缩放。这样就会保证字体或图片不被拉伸。

    function keepFit(designWidth, designHeight, renderDom) {
      let clientHeight = document.documentElement.clientHeight;
      let clientWidth = document.documentElement.clientWidth;
      let scale = 1;
      if (clientWidth / clientHeight < designWidth / designHeight) {
        scale = (clientWidth / designWidth)
        document.querySelector(renderDom).style.height = `${clientHeight / scale}px`;
      } else {
        scale = (clientHeight / designHeight)
        document.querySelector(renderDom).style.width = `${clientWidth / scale}px`;
      }
      document.querySelector(renderDom).style.transform = `scale(${scale})`;
    }
    

方案二:大屏vw和vh适配方案:

按照设计稿的尺寸,将px按比例计算转为vwvh

假设设计稿尺寸为1920*1080(做之前一定问清楚UI设计稿的尺寸),即: 网页宽度=1920px,网页高度=1080px;我们都知道,网页宽度=100vw,网页宽度=100vh。
所以,在1920x*1080px的屏幕分辨率下1920px = 100vw,1080px = 100vh。这样一来,以一个宽300px和200px的div来说,其作所占的宽高,以vw和vh为单位,计算方式如下:

vwDiv = (300px / 1920px ) * 100vw;
vhDiv = (200px / 1080px ) * 100vh;

所以,就在1920*1080的屏幕分辨率下,计算出了单个div的宽高当屏幕放大或者缩小时,div还是以vw和vh作为宽高的,就会自动适应不同分辨率的屏幕。

但是每次都手动计算时不现实的,所以,需要封装一个方法,让它帮我自动计算:

// 使用scss的math函数,https://sass-lang.com/documentation/breaking-changes/slash-div
@use "sass:math";

//默认设计稿的宽度
$designWidth: 1920;
//默认设计稿的高度
$designHeight: 1080;

//px转为vw的函数
@function vw($px) {
  @return math.div($px , $designWidth) * 100vw;
}

//px转为vh的函数
@function vh($px) {
  @return math.div($px , $designHeight) * 100vh;
}

vue.config.js(只展示了与本方法有关的配置)

module.exports = {
  ...,
  css: {
    loaderOptions: {
      //全局配置utils.scss,详细配置参考vue-cli官网
      sass: {
        prependData: '@import "@/styles/utils.scss";',
      }
    },
  },
};

.vue文件中使用

<script>
  export default{
    name: "Index",
  }
</script>

<style lang="scss" scoped="scoped">
  /* 直接使用vw和vh函数,将像素值传进去,得到的就是具体的vw vh单位*/
  .chart-wrapper{
    width: vw(400);
    height: vh(300);
    font-size: vh(16);
    background-color: black;
    margin: vw(20);
    border: vh(2) solid red;
  }
</style>
特殊的使用情况

有的时候可能不仅在.vue文件中使用,比如在js中动态创建的DOM元素
它可能是直接渲染到html里面的

let oDiv = document.createElement('div')
document.body.appendChild(oDiv)

这样的话,我用了以下两种处理方式,来给创建的div设置样式
1. 定义一些全局的class
新建一个global.scss文件,在main.js中引入
global.scss

.global-div{
  width: vw(300);
  height: vw(200);
  background-color: green;
}

main.js

import './styles/global.scss'

使用时,给创建的div设置className,

let oDiv = document.createElement('div')
oDiv.className = "global-div"

还有echarts图表可能会需要自适应字体

/* Echarts图表尺寸自适应 */
export const fitChartFontSize = (size, defaultWidth = 1920) => {
  let clientWidth = window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth;
  if (!clientWidth) return size;
  let scale = (clientWidth / defaultWidth);
  return Number((size*scale).toFixed(3));
}

使用

// Options.js

//坐标轴刻度标签的相关设置
yAxis: {
  axisLabel: {
    textStyle: {
      color: "rgba(255, 255, 255, 0.3)"
    },
    fontSize: fitChartFontSize(10)
  },
},

2. 第三方自动适配插件库Postcss + 转换成vw的插件

一、安装postcss
npm install postcss -S

二、使用npm/yarn安装postcss插件
$ npm install postcss-px-to-viewport --save-dev
$ yarn add -D postcss-px-to-viewport

配置参数

//默认参数options:
{
  unitToConvert: 'px', //(String) 需要转换的单位,默认为"px"
  viewportWidth: 320,
  //(Number) 设计稿的视口宽度
  unitPrecision: 5,    //(Number) 单位转换后保留的精度
  propList: ['*'],     //(Array) 能转化为vw的属性列表
  viewportUnit: 'vw',  //(String) 希望使用的视口单位
  fontViewportUnit: 'vw', //(String) 字体使用的视口单位
  selectorBlackList: [], // (Array) 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。
  minPixelValue: 1,
  mediaQuery: false,
  replace: true, //(Boolean)是否直接更换属性值,而不添加备用属性
  exclude: undefined, //(Array or Regexp) 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
  include: undefined, //(Array or Regexp) 如果设置了include,那将只有匹配到的文件才会被转换,例如只转换 'src/mobile' 下的文件 (include: /\/src\/mobile\//)
  landscape: false,
  landscapeUnit: 'vw',
  landscapeWidth: 568
}

示例

/* 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;
}

使用PostCss配置文件时,在postcss.config.js添加如下配置

module.exports = {
  plugins: {
    // ...
    'postcss-px-to-viewport': {
      // options
    }
  }
}

方案三:可视化图形可以使用DataV

根据固定的比例开发,界面显示用100%的开发方式,内部字体和尺寸都可以函数方法适配,其实就是另一种方式的transform适配方案

// 使用标签
<dv-full-screen-container>
  ...插入内容
  <div></div>
</dv-full-screen-container>

方案四:采用amfe-flexible结合postcss插件(postcss-pxtorem):

postcss.config.js添加如下配置

module.exports = {
  plugins: {
    // ...
    'postcss-pxtorem': {
      rootValue: 192, //PC端(1920的UI图)——换算基数(分辨率÷10)
      propList: ['*'],
      unitPrecision: 5, //保留rem小数点多少位
      exclude: /(node_module)/ //默认false,可以(reg)利用正则表达式排除某些文件夹的方法,例如/(node_module)/ 。如果想把前端UI框架内的px也转换成rem,请把此属性设为默认值
      selectorBlackList: ['.el-'], //要忽略并保留为px的选择器
      replace: false, // (布尔值)替换包含REM的规则,而不是添加回退。
      mediaQuery: false, //媒体查询( @media screen 之类的)中不生效
      minPixelValue: 12, //px小于12的不会被转换
  	},
  }
}

如果个别属性不想px转化rem,可以写成14Px或者14PX的单位

// 安装amfe-flexible
npm install amfe-flexible -S
# 主要作用是设置更目录fontsize大小 rem才会有基数计算

或者也可以手动写utilRem.js

// 动态获取实际文档宽高并设置body缩放系数
// 基准大小
const baseSize = 14
// 设置 rem 函数
function setRem () {
  // 当前页面宽度相对于 1920宽的缩放比例,可根据自己需要修改。
  const scale = document.documentElement.clientWidth / 1920
  // 设置页面根节点字体大小
  document.documentElement.style.fontSize = (baseSize * Math.min(scale, 2)) + 'px'
}
// 初始化
setRem()
// 改变窗口大小时重新设置 rem
window.onresize = function () {
  setRem()
}
  • 17
    点赞
  • 121
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值