vue中如何使用ECharts画图并实现自适应

4,211 阅读1分钟

「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战」。

1. vue 中如何使用 ECharts

1.1.安装 ECharts

npm install echarts --save

1.2.引入 ECharts

在 vue 项目的 main.js 文件中全局全部引入

// 全部引入echarts
import * as echarts from 'echarts'
Vue.prototype.$echarts = echarts

以上引入方式是将 ECharts 中所有的图表和组件全部引入,这会导致打包时文件过大,所以我们也可以根据实际需要在需要画图的 .vue 文件中按需引入

// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
import * as echarts from 'echarts/core'
// 引入柱状图图表,图表后缀都为Chart,也可以引入其他图表
import { BarChart, PieChart, LineChart } from 'echarts/charts'
// 引入提示框,标题,直角坐标系组件,组件后缀都为 Component
import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  LegendComponent,
  DatasetComponent
} from 'echarts/components'
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
import { CanvasRenderer } from 'echarts/renderers'
// 注册必须的组件
echarts.use([
  TitleComponent,
  TooltipComponent,
  GridComponent,
  BarChart,
  PieChart,
  LineChart,
  CanvasRenderer,
  LegendComponent,
  DatasetComponent
])

1.3.使用 ECharts 画图

在页面标签中新建一个 id(必需)为 main 的固定宽高的 div

<template>
  <div class="app-container">
    <div id="main" style="width:800px;height:700px" />
  </div>
</template>

在 methods 写好画图的方法,该方法包含一些图表的配置信息

methods: {
    initChart(domName, title) {
      var myChart = this.$echarts.init(domName)
      var option = {
        title: {
          text: title
        },
        legend: {
          data: ['销量']
        },
        xAxis: {
          data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
        },
        yAxis: {},
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          }
        ]
      }

      option && myChart.setOption(option)
    }
  }
}

mounted 中调用该方法进行画图

mounted() {
   this.initChart(document.getElementById('main'), '111111111')
},

页面效果如图

以上就是 vue 中使用 ECharts 画图的基本方法,具体也可参考官方

但是光基本的使用方法,往往无法满足项目的需求,一般在项目中我们需要实现图表的自适应或者多图,嵌套画图,实时数据画图,循环画图等等。下面我们先来研究如何实现图表的自适应。

2.ECharts 图表自适应

2.1.前提

将图表容器的宽高设置为百分比的形式,完整代码如下:

<template>
  <div class="app-container">
    <div class="chartCon">
      <div class="normalStyle">
        <div id="main1" style="width:100%;height:100%" />
      </div>
      <div class="normalStyle">
        <div id="main2" style="width:100%;height:100%" />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
    }
  },

  mounted() {
    this.initChart(document.getElementById('main1'), '111111111')
    this.initChart(document.getElementById('main2'), '222222222')
  },

  methods: {
    initChart(domName, title) {
      var myChart = this.$echarts.init(domName)
      var option = {
        title: {
          text: title
        },
        legend: {
          data: ['销量']
        },
        xAxis: {
          data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
        },
        yAxis: {},
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          }
        ]
      }

      // 绘制图表
      option && myChart.setOption(option)
    }
  }
}
</script>

<style lang="scss">
.chartCon{
  width: calc(100% - 40px);
  background-color: #fff;
  position: absolute;
  left: 20px;
  right: 20px;
  bottom: 20px;
  top: 20px;
  .normalStyle{
    width:50%;
    height:100%;
    float:left;
    border:1px solid #eee
  }
}
</style>

页面渲染存在以下问题

1)初次渲染时,图表高度不是按设置的100%来显示,如图:

2)需要手动点一下浏览器的刷新后才正常显示,如图:

3)图表没有随着浏览器窗口的大小而自适应显示,如图:

2.2.实现自适应

方法一、使用 window.onresize(官方)

基于前提中的代码,更改 initChart 方法的代码如下:

initChart(domName, title) {
      var myChart = this.$echarts.init(domName)
	
      // 自适应
      window.onresize = function() {
        myChart.resize()
      }

      var option = {
        title: {
          text: title
        },
        legend: {
          data: ['销量']
        },
        xAxis: {
          data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
        },
        yAxis: {},
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          }
        ]
      }

      // 绘制图表
      option && myChart.setOption(option)
    }

存在的问题:

1)初次渲染时,图表高度仍然不是按设置的100%来显示,如图:

2)仍然需要手动刷新一次后才正常显示,如图:

2)多个图表的时候就只有最后一个自适应生效了,onresize会被覆盖,如图:

方法二、使用 window.addEventListener 添加 resize 方法

基于前提中的代码,更改 initChart 方法的代码如下:

initChart(domName, title) {
      var myChart = this.$echarts.init(domName)
      
      //自适应
      var listener = function() {
        myChart.resize()
      }
      window.addEventListener('resize', listener)

      var option = {
        title: {
          text: title
        },
        legend: {
          data: ['销量']
        },
        xAxis: {
          data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
        },
        yAxis: {},
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          }
        ]
      }

      // 绘制图表
      option && myChart.setOption(option)
    }

存在的问题:

1)初次渲染时,图表高度仍然不是按设置的100%来显示,如图:

2)仍然需要手动刷新一次后才正常显示,如图:

3)虽然基本实现了图表的自适应,但是当 vue 页面路由跳转到下一个页面时,上一个页面的 onresize 方法仍会继续执行。

方法三、通过封装自适应方法 esresize 实现

参考:blog.csdn.net/qq_31967985…

esresize.js 如下:

var EleResize = {
  _handleResize: function(e) {
    var ele = e.target || e.srcElement
    var trigger = ele.__resizeTrigger__
    if (trigger) {
      var handlers = trigger.__z_resizeListeners
      if (handlers) {
        var size = handlers.length
        for (var i = 0; i < size; i++) {
          var h = handlers[i]
          var handler = h.handler
          var context = h.context
          handler.apply(context, [e])
        }
      }
    }
  },
  _removeHandler: function(ele, handler, context) {
    var handlers = ele.__z_resizeListeners
    if (handlers) {
      var size = handlers.length
      for (var i = 0; i < size; i++) {
        var h = handlers[i]
        if (h.handler === handler && h.context === context) {
          handlers.splice(i, 1)
          return
        }
      }
    }
  },
  _createResizeTrigger: function(ele) {
    var obj = document.createElement('object')
    obj.setAttribute('style',
      'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden;opacity: 0; pointer-events: none; z-index: -1;')
    obj.onload = EleResize._handleObjectLoad
    obj.type = 'text/html'
    ele.appendChild(obj)
    obj.data = 'about:blank'
    return obj
  },
  _handleObjectLoad: function(evt) {
    this.contentDocument.defaultView.__resizeTrigger__ = this.__resizeElement__
    this.contentDocument.defaultView.addEventListener('resize', EleResize._handleResize)
  }
}
if (document.attachEvent) { // ie9-10
  EleResize.on = function(ele, handler, context) {
    var handlers = ele.__z_resizeListeners
    if (!handlers) {
      handlers = []
      ele.__z_resizeListeners = handlers
      ele.__resizeTrigger__ = ele
      ele.attachEvent('onresize', EleResize._handleResize)
    }
    handlers.push({
      handler: handler,
      context: context
    })
  }
  EleResize.off = function(ele, handler, context) {
    var handlers = ele.__z_resizeListeners
    if (handlers) {
      EleResize._removeHandler(ele, handler, context)
      if (handlers.length === 0) {
        ele.detachEvent('onresize', EleResize._handleResize)
        delete ele.__z_resizeListeners
      }
    }
  }
} else {
  EleResize.on = function(ele, handler, context) {
    var handlers = ele.__z_resizeListeners
    if (!handlers) {
      handlers = []
      ele.__z_resizeListeners = handlers

      if (getComputedStyle(ele, null).position === 'static') {
        ele.style.position = 'relative'
      }
      var obj = EleResize._createResizeTrigger(ele)
      ele.__resizeTrigger__ = obj
      obj.__resizeElement__ = ele
    }
    handlers.push({
      handler: handler,
      context: context
    })
  }
  EleResize.off = function(ele, handler, context) {
    var handlers = ele.__z_resizeListeners
    if (handlers) {
      EleResize._removeHandler(ele, handler, context)
      if (handlers.length === 0) {
        var trigger = ele.__resizeTrigger__
        if (trigger) {
          trigger.contentDocument.defaultView.removeEventListener('resize', EleResize._handleResize)
          ele.removeChild(trigger)
          delete ele.__resizeTrigger__
        }
        delete ele.__z_resizeListeners
      }
    }
  }
}
export { EleResize }

vue 文件中引入 esresize.js

import { EleResize } from '@/utils/esresize'// 图表自适应

使用:基于前提中的代码,更改 initChart 方法的代码如下:

initChart(domName, title) {
      var myChart = this.$echarts.init(domName)
      
      // 自适应
      var listener = function() {
        myChart.resize()
      }
      EleResize.on(domName, listener)

      var option = {
        title: {
          text: title
        },
        legend: {
          data: ['销量']
        },
        xAxis: {
          data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
        },
        yAxis: {},
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          }
        ]
      }

      // 绘制图表
      option && myChart.setOption(option)
 }

使用该方法以上所有存在的问题都被解决了。

2.3.补充

对于将图表容器宽高设置成百分比而造成的初次渲染时图表容器高度不生效的问题(以上方法一和方法二的问题1),还可以通过监听浏览器窗口变化动态设置图表容器大小为 window.innerWidth 和 window.innerHeight 来解决,可以搭配自适应来一起使用,只不过这样比较繁琐,不推荐使用,但是也可以参考一下实现方式。

具体代码如下:

<template>
  <div class="app-container">
    <div class="chartCon">
      <div class="normalStyle">
        <div id="main1" :style="'width:' + normalWidth/2 + 'px;height:' + normalHeight + 'px'" />
      </div>
      <div class="normalStyle">
        <div id="main2" :style="'width:' + normalWidth/2 + 'px;height:' + normalHeight + 'px'" />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      screenHeight: window.innerHeight,
      screenWidth: window.innerWidth,
      normalHeight: window.innerHeight * 0.9,
      normalWidth: window.innerWidth
    }
  },

  watch: {
    // 监听screenHeight和screenWidth从而动态修改图表容器宽高
    screenHeight(val) {
      this.screenHeight = val
      this.normalHeight = this.screenHeight * 0.9
    },
    screenWidth(val) {
      this.screenWidth = val
      this.normalWidth = this.screenWidth
    }
  },

  mounted() {
    // 画图
    this.initChart(document.getElementById('main1'), '111111111')
    this.initChart(document.getElementById('main2'), '222222222')

    // 浏览器窗口变化触发,获取当前浏览器窗口大小
    window.onresize = () => {
      return (() => {
        window.screenHeight = window.innerHeight
        window.screenWidth = window.innerWidth
        this.screenHeight = window.screenHeight
        this.screenWidth = window.screenWidth
      })()
    }
  },

  methods: {
    initChart(domName, title) {
      var myChart = this.$echarts.init(domName)

      // 自适应
      var listener = function() {
        myChart.resize()
      }
      window.addEventListener('resize', listener)

      var option = {
        title: {
          text: title
        },
        legend: {
          data: ['销量']
        },
        xAxis: {
          data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
        },
        yAxis: {},
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          }
        ]
      }

      // 绘制图表
      option && myChart.setOption(option)
    }
  }
}
</script>

<style lang="scss">
.chartCon{
  width: calc(100% - 40px);
  background-color: #fff;
  position: absolute;
  left: 20px;
  right: 20px;
  bottom: 20px;
  top: 20px;
  .normalStyle{
    width:50%;
    height:100%;
    float:left;
    border: 1px solid #eee;
  }
}
</style>

初次渲染时页面完好,如图:

3.总结

以上内容只是针对在 div 中画图时存在的一些问题和解决办法,貌似在 Tabs 标签页中画图又会出现别的问题,继续研究,感谢各位大佬!