• 可视化大屏自适应方案


    前言

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

    方案

    方案一:利用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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    第二步:根据设计同学提供的设计图可以计算出每部分区域的百分比,例如总尺寸是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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    第三步:基于第二步,需要根据大屏具体尺寸计算缩放比例,以及设置缩放比例,需要注意的是,绑定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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    引用方式和最终呈现效果


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

    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})`;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13

    方案二:大屏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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

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

    module.exports = {
      ...,
      css: {
        loaderOptions: {
          //全局配置utils.scss,详细配置参考vue-cli官网
          sass: {
            prependData: '@import "@/styles/utils.scss";',
          }
        },
      },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    .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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    特殊的使用情况

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

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

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

    .global-div{
      width: vw(300);
      height: vw(200);
      background-color: green;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    main.js

    import './styles/global.scss'
    
    • 1

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

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

    还有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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用

    // Options.js
    
    //坐标轴刻度标签的相关设置
    yAxis: {
      axisLabel: {
        textStyle: {
          color: "rgba(255, 255, 255, 0.3)"
        },
        fontSize: fitChartFontSize(10)
      },
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    配置参数

    //默认参数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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    示例

    /* 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

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

    module.exports = {
      plugins: {
        // ...
        'postcss-px-to-viewport': {
          // options
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

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

    // 使用标签
    <dv-full-screen-container>
      ...插入内容
      <div>div>
    dv-full-screen-container>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    方案四:采用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的不会被转换
      	},
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

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

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

    或者也可以手动写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()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    常见的浏览器跨域解决方法
    Vue系列之入门篇
    Java.lang.Class类 getProtectionDomain()方法有什么功能呢?
    Kafka消息队列详解
    APP逆向案例之(二)对加固APP进行分析和破解
    Synchronized
    Java 后端实现鼠标滚动,点击事件
    欧盟ROHS2.0检测标准费用,rohs认证是什么,rohs测试
    互融云贷款中介平台搭建,助力企业实现数字化智能办公
    DELL设备维保查询方法
  • 原文地址:https://blog.csdn.net/Pentoncos/article/details/128000854