Vue+ECharts爬坑回忆录

·  阅读 1503

项目概述

1. 依赖包版本

// package.json
{
  // ...
  "dependencies": {
    // ...
    "echarts": "^4.8.0",
    "vue": "^2.6.10",
    // ...
  },
  // ...  
}
复制代码

2. 业务说明

简要分析下公司目前的数据大屏情况:

  1. 除了地图模块数据量相对复杂些,其他模块展示的数据量可控;
  2. 项目设置了专门的展厅,页面需要长时间启用,页面时有崩溃现象发生;
  3. 页面投放方式为主机加载后,有场景会涉及需要使用浏览器内置的缩放功能展示。

项目的页面缩放方案参考了 百度云 页面的实现思路,按照固定的宽高比(如:16:9)来计算出最合适的缩放比进行缩放渲染

<template>
  <div class="scale-box" :style="{width: cw + 'px', height: ch + 'px'}">
    <div class="scale-container" :style="{transform: 'scale(' + scale + ')', width: width + 'px', height: height + 'px'}">
      <slot></slot>
    </div>
  </div>
</template>
复制代码
// 容器宽高
let cw = 0;
let ch = 0;

// UI规范宽高
let width = 1920;
let height = 1080;
let scale = 1;

// 可视区宽高比例
let ww = window.innerWidth / this.width;
let wh = window.innerHeight / this.height;

// 计算实际最合适缩放比:以小者为缩放基准,内容不足处留白处理,确保内容展示的完整性。原理同背景图的background-size属性。
scale = ww < wh ? ww : wh;

// 更新容器渲染宽高
cw = this.width * scale
ch = this.height * scale
复制代码

一、Canvas 渲染还是 SVG 渲染

1. 理论梳理

  • Canvas 更适合绘制图形元素数量非常大的图表,也利于实现某些 视觉特效

    如:热力图、地理坐标系或平行坐标系上的大规模线图或散点图等。

    // 使用 Canvas 渲染器(默认)
    var chart = echarts.init(containerDom, null, {renderer: 'canvas'});
    // 等价于:
    var chart = echarts.init(containerDom);
    复制代码
  • SVG 内存占用更低、渲染性能略高、用户使用浏览器内置的缩放功能时不会模糊

    使用 Canvas 渲染器和 SVG 渲染器绘制中等数据量的折、柱、饼图,SVG 渲染器相比 Canvas 渲染器在移动端的总体表现更好。但是在另一些数据量较大或者有图表交互动画的场景中,目前的 SVG 渲染器的性能还比不过 Canvas 渲染器。

    // 使用 SVG 渲染器
    var chart = echarts.init(containerDom, null, {renderer: 'svg'});
    复制代码
  • 综上所述,选择哪种渲染器,需要根据 软硬件环境数据量功能需求 来综合考虑决定。

    业务场景(↓) \ ECharts渲染器(→) Canvas SVG
    软硬件环境较好
    数据量不大
    软硬件环境较差
    数据量不大
    多ECharts实例
    浏览器易崩溃
    ×
    数据量很大
    交互较多
    ×
  • 注意 目前的 SVG 版中,富文本、材质功能尚不支持

2. 项目分析

基于现有缩放方案,UI 稿是按照 1920 × 1080 的宽高像素输出,购置的硬件规格也是一个屏幕为 1920 × 1080 像素的宽高尺寸。

  • 如果一个页面在一个硬件屏幕上展示时,无需缩放,展示效果接近输出的 UI 稿。
  • 但实际存在九个硬件屏幕拼接成一个大屏幕来渲染一个页面的场景(此时相当于页面被二次放大了 9 倍。最终,页面元素渲染模糊)。

综上所述,公司该数据大屏项目最终选择的 ECharts 渲染器为 SVG

// svg渲染
var chart = echarts.init(containerDom, null, {renderer: 'svg'});
复制代码

3. 来个扩展

关于 ECharts 元素渲染模糊问题,翻看 ECharts文档 有发现一个配置参数:devicePixelRatio设备像素比,默认取浏览器的值 window.devicePixelRatio)。

试问,开发中是否可以保留使用 Canvas 渲染,通过设置 devicePixelRatio 来解决元素渲染模糊这个问题呢?

思路如下:

  1. 保留 ECharts 默认渲染器(Canvas);
  2. 设置 devicePixelRatio 为一个较大值,如:9
  3. 使用浏览器内置的缩放功能查看页面元素渲染结果是否模糊。如果模糊,那存在对应场景的业务代码就只能采用 SVG 渲染模式;如果不模糊,那就可以继续使用 Canvas 渲染啦,只是加多一个 devicePixelRatio 参数值设置而已。

思路有了,那我们测试下吧~

  1. 默认渲染器,默认设备像素比

    // 默认渲染器,默认设备像素比
    var chart = echarts.init(containerDom);
    复制代码

    renderer: 'canvas', devicePixelRadio: 1

  2. 默认渲染器,设备像素比设置为较大值

    // 默认渲染器,设备像素比设置为较大值
    var chart = echarts.init(containerDom, null, {devicePixelRatio: 9});
    复制代码

    renderer: 'canvas', devicePixelRadio: 9

  3. SVG 渲染

    // svg渲染
    var chart = echarts.init(containerDom, null, {renderer: 'svg'});
    复制代码

    renderer: 'svg'

由此说明,如若是单纯考虑浏览器内置的缩放功能的因素,使用 SVG 渲染并不是必须的。而且仔细看会发现,Canvas 渲染的效果更丰富:当前高亮模块的渐变竖线,Canvas 渲染有,而SVG 渲染没有。

二、慎用有色原生表格渲染数据

1. 问题陈述

基于现有缩放方案,使用 原生表格qb-origin-table)渲染数据,如若表格行(tr)是有背景色的,使用该页面缩放方案即使表格的边框合并在一起(border-collapse: collapse;),表格边框之间的间距(border-spacing)为 0,无边框(border: 0 none;),边框色透明(border-color: transparent;),单元格之间仍可见一条黑线

黑线bug图

最终不得不借用无意义的 div 模拟实现表格(qb-imitate-table)来兼容。

2. 原理分析

  1. CSS3 transform 属性 scale 改变的不是元素的宽高,而是 X, Y 轴的刻度。

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>CSS3 transform scale解析</title>
      <style>
        .box-origin {
          width: 100px;
          height: 100px;
          margin: 150px;
          background: lightgray;
          color: black;
        }
    
        .box-scale {
          width: 100px;
          height: 100px;
          margin: 150px;
          background: lightgray;
          color: black;
          transform: scale(2);
        }
      </style>
    </head>
    
    <body>
      <div class="box-origin">原始div块</div>
    
      <div class="box-scale">原始div块scale放大后的div块</div>
    </body>
    
    </html>
    复制代码

    原理分析图示

  2. HTML 采用的是窗口坐标系,以参考对象的元素盒子左上角为坐标原点,x 轴向右,y 轴向下,坐标值对应像素值。

    参考对象通常是最接近分析元素的 positionstatic 的元素。

    • 保留等比缩放

      开启缩放效果图

    • 重置缩放比例为默认值

      关闭缩放效果图

    尝试对表格行重置缩放比例(transform: scale(1);),黑线并未消除。考虑到坐标系因素,往上一层层重置,直到重置到最外层的缩放比,黑线终于没有了!

三、Vue 中推荐 ECharts 组件模板

<template>
  <div ref="moreDataLineChart" style="width: 100%; height: 100%;"></div>
</template>

<script>
/**
* @description: 多折线ECharts组件
* @update: 2021-02-02 11:03:30
* @author: Ada.H
*/

export default {
  name: 'moreDataLineChart',
  props: {
    // 数据集合
    seriesData: {
      type: Array,
      default: () => {
        return [];
      },
    },
    // other props here ...
  },
  data() {
    return {
      // 组件实例对象
      myChart: null,
      // 组件配置参数
      options: {},
    };
  },
  mounted() {
    this.init();
  },
  beforeDestroy() {
    this.destroyEchart();
  },
  watch: {
    seriesData: {
      handler() {
        this.$nextTick(() => {
          this.update();
        });
      },
      deep: true,
    },
  },
  methods: {
    // 初始化ECharts组件
    init() {
      this.destroyEchart();

      if (!this.myChart) {
        this.myChart = this.$echarts.init(this.$refs.moreDataLineChart, null, {
          renderer: 'svg',
        });
      }
      
      this.update();
    },
    // 更新组件数据
    update() {
      // 逻辑处理组件options参数
      const options = {
        series: this.seriesData,
        // other options here ...
      };
      this.options = options;

      // 确保dom存在
      this.$nextTick(() => {
        // 清除画布
        this.myChart.clear();
        // 调用ECharts组件setOption更新
        this.myChart.setOption(this.options, true);
        // 更新动画
        this.mixin_loop_animation(this.options, this.myChart, true).then(
          (res) => {
            if (res) {
              this.mixin_loop_animation(this.options, this.myChart, false);
            }
          }
        );
      });
    },
    // 销毁ECharts实例
    destroyEchart() {
      if (this.myChart) {
        this.myChart.clear();
        this.myChart.dispose();
        this.myChart = null;
      }
    },
    // other methods here ...
  },
};
</script>
复制代码
  1. 组件销毁时要记得清理 ECharts 实例,及时释放应用内存。
  2. 组件初始化 init 和组件数据更新 update 相互独立,保证一个组件从创建到销毁的整个生命周期只初始化一次 ECharts 实例,节省程序性能消耗。
  3. 借由 Vue 的 深度 watch 来同步更新 ECharts 实例视图,而非粗暴的 v-if,避免引发内存泄漏,导致页面崩溃问题。Vue.js 避免内存泄漏
分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改