Vue 封装 BaseChart 组件,并添加自动高亮功能

1,340 阅读2分钟

背景

Vue项目(cli3.x)中使用了Echarts(4.8.0)图表,有自动轮播高亮的需求,所以就找到了下官方文档Api中的dispatchAction其他人的实现,做出了初步的实现,以供大家参考。

问题:

1、定时器的存储和控制

调用 autoHover 方法,传入 chart 对象 —— 由 echarts.init(dom) 实例化的对象,给 chart 对象绑定一个 _timer 属性,外部可直接通过 chart 对象访问该属性并操作,每次定时器触发时,用一个变量存储 chart._timer,保证在 chart 销毁后销毁定时器

2、鼠标事件的处理

在这儿主要针对鼠标移入(mouseover)和鼠标移出(mouseout)事件进行处理,当鼠标移入时关闭定时器,鼠标移出时恢复自动轮播,顺序接上一自动轮播之后。

鼠标移入只有移入到饼图和柱状图的数据上才有效,最后解决方案直接给整个图表增加了鼠标移入事件,通过 chart.getZr().on 注册鼠标移出事件,getZr() 是直接针对画布的。

代码

BaseChart.vue

<template>
  <div ref="chart" :style="{ width, height }" @mouseover="clearTimer" />
</template>

<script>
import echarts from 'echarts'
// import { debounce } from 'lodash-es'
import { autoHover } from './auto-chart'

export default {
  name: 'BaseChart',
  props: {
    width: {
      type: String,
      default: '100%'
    },
    height: {
      type: String,
      default: '100%'
    }
  },
  data () {
    return {
      chart: null
    }
  },
  mounted () {
    window.addEventListener('resize', this.handleResize)
  },
  methods: {

    /** 窗口变化事件 */
    handleResize () {
      this.chart && this.chart.resize()
    },

    /** 高亮索引切换 */
    handleHoverIndexChange (index) {
      this.$emit('curDataIndex', index)
    },

    setChart ({ options = {}, isAutoHover = false, isClear = false, seriesIndex = 0 }) {
      this.$nextTick(() => {
        if (!this.chart) {
          this.chart = echarts.init(this.$refs.chart)
        }
        this.clearTimer()
        if (isAutoHover) {
          autoHover({
            chart: this.chart,
            seriesIndex,
            length: options.series[seriesIndex].data.length,
            cb: this.handleHoverIndexChange
          })
        }
        isClear && this.chart.clear()
        this.chart.setOption(options)
        this.chart.resize()
      })
    },

    /** 外部调用的 自动轮播方法 */
    autoHover (params) {
      this.$nextTick(() => {
        autoHover({ chart: this.chart, ...params })
      })
    },

    /** 清除定时器 */
    clearTimer () {
      if (this.chart && this.chart._timer) {
        clearInterval(this.chart._timer)
        // clearTimeout(this.chart._timer)
        this.chart._timer = null
      }
     }
  },
  beforeDestroy () {
    this.clearTimer()
    /** 移除事件 */
    window.removeEventListener('resize', this.handleResize)
  }
}
</script>

auto-chart.js

/**
* 清除定时器
* @param {Echart} chart
*/
const clearTimer = chart => {
  if (!chart && !chart._timer) return
  clearInterval(chart._timer)
  chart._timer = null
}

const sendChartDataIndex = (n, cb) => {  cb(n) }

/**
* 设置 chart 的高亮和 tip 显示
* @param {Echart} chart
* @param {number/array} seriesIndex series索引
* @param {number} index 数据索引
*/
export const setChartLightAndTip = (chart, seriesIndex = 0, index = 0) => {
  if (!chart) return false
  // 取消高亮指定的数据图形
  chart.dispatchAction({
    type: 'downplay',
    seriesIndex
  })
  // 高亮
  chart.dispatchAction({
    type: 'highlight',
    seriesIndex,
    dataIndex: index
  })
  // 显示tip
  chart.dispatchAction({
    type: 'showTip',
    seriesIndex,
    dataIndex: index
  })
}
/**
* chart自动循环高亮
* @param {Echart} chart
* @param {number} length 数据长度
* @param {number} interval 切换间隔时间(毫秒)
* @param {number/array} seriesIndex series索引
* @param {number} defaultIndex 默认高亮索
* 参考:https://www.cnblogs.com/liuwei54/p/10496555.html
*/
export const autoHover = ({
  chart,
  length = 0,
  interval = 2000,
  seriesIndex = 0,
  defaultIndex = 0,
  cb }) => {

  let _index = defaultIndex
  let _timer = null

  /** 如果 chart 不存在 */
  if (!chart) {
    return void 0
  }

  /** 先清除事件 */
  chart.off('mouseout')

  /** 定时器存在 */
  if (chart._timer) {
    clearTimer(chart)
  }

  /** 定时器回调函数 */
  const timerCB = () => {
    if (chart) {
      _timer = chart._timer
    }

    // 如果 chart 不存在,则清空定时器
    if (!chart) {
      // clearTimer(chart)
      clearInterval(_timer)
      _timer = null
    }

    // 切换高亮
    setChartLightAndTip(chart, seriesIndex, _index)
    // 发送 切换高亮后 事件
    sendChartDataIndex(_index, cb)
    _index++
    // 归零
    if (_index >= length) _index = 0
  }

  // 设置定时器
  chart._timer = setInterval(() => {
    timerCB()
  }, interval)

  // 处理鼠标移入事件
  // chart.getZr().on('mouseover', params => {
    //   console.log('移入')
    //   clearTimer(chart)
    //   _index = params.dataIndex
    //   sendChartDataIndex(_index, cb)
    //   setChartLightAndTip(chart, seriesIndex, _index)
  // })
  // 处理鼠标移出事件
  chart.getZr().on('mouseout', () => {
    clearTimer(chart)
    chart._timer = setInterval(() => {
      timerCB()
    }, interval)
  })
}

实现得比较潦草,希望能给大家提供一些思路,有想过项目中利用es6的Class,实现一个Chart类,统一维护Chart相关基础的属性和行为(方法),在计划中了,如果有什么建议可以评论区留言。