ECharts封装及配置整理

发布时间:2023年12月20日

1 认识ECharts

ECharts,一个基于 JavaScript 的开源可视化图表库

ECharts官网

国内Echarts使用手册网站:
https://www.w3cschool.cn/echarts_tutorial/echarts_tutorial-d5b128yu.html

示例网站:
https://www.isqqw.com/
http://chart.majh.top/
https://www.ppchart.com/#/

2 为什么需要封装echarts

  • 每个开发者在制作图表时都需要从头到尾书写一遍完整的option配置,十分冗余
  • 在同一个项目中,各类图表设计十分相似,甚至是相同,没必要一直做重复工作
  • 可能有一些开发者忘记考虑echarts更新数据的特性,以及窗口缩放时的适应问题。这样导致数据更新了echarts视图却没有更新,窗口缩放引起echarts图形变形问题
2.0 Echarts/mixins/resize.js
export default {
  mounted() {
    window.addEventListener('resize', this.resizeHandler)
  },
  activated() {
    this.resizeHandler()
  },
  methods: {
    resizeHandler() {
      if (this['myChart' + this.domId]) {
        this['myChart' + this.domId].resize()
      }
    }
  }
}
2.1 封装柱状/折线 一体图(1网格)
<!--柱状/折线 一体图(1grid)-->
<template>
  <div
    :id="domId"
    :key="domId"
    :style="{ height: height, width: width,backgroundColor: backgroundColor }"
  />
</template>
<script>
import * as echarts from 'echarts'
import resize from './mixins/resize'
import 'echarts/lib/component/dataZoom'

export default {
  mixins: [resize],
  props: {
    domId: { type: String, default: 'chartId' },
    height: { type: String, default: '300px' },
    width: { type: String, default: '100%' },
    backgroundColor: { type: String, default: '#fff' },
    data: { type: Object, default: null }
  },
  watch: {
    data: {
      handler(val) {
        // 这里使用nextTick解决Error: Initialize failed: invalid dom
        this.$nextTick(() => {
          this.initChart(val)
        })
      },
      deep: true
    }
  },
  mounted() {
    this.initChart(this.data)
  },
  methods: {
    initChart(data) {
      if (!data) {
        return false
      }

      // const yAxis = this.setInterval(data.yAxis, data.series)
      this['myChart' + this.domId] = echarts.init(
        document.getElementById(this.domId)
      )

      this['myChart' + this.domId].clear() // 清空画布,防止缓存

      this['myChart' + this.domId].setOption({
        grid: data.grid || {
          left: '10%',
          top: '15%',
          right: '10%',
          bottom: '25%'
        },
        toolbox: data.toolbox,
        axisPointer: data.axisPointer || null,
        title: data.title
          ? data.title
          : {
            text: null,
            textStyle: {
              fontSize: 10,
              color: '#666666'
            },
            left: '5%'
          },
        legend: data.legend
          ? data.legend
          : {
            data: data.legendData,
            top: 0,
            textStyle: {
              fontSize: 9,
              color: '#666666'
            },
            formatter: function(name) {
              return name.length > 12 ? name.substr(0, 12) + '...' : name
            }
          },
        tooltip: data.tooltip
          ? data.tooltip
          : {
            trigger: 'axis'
          },
        xAxis: data.xAxis,
        // yAxis: yAxis,
        yAxis: data.yAxis,
        dataZoom: data.dataZoom,
        visualMap: data.visualMap,
        series: data.series
      })
    }
    // 根据data来判断y轴的间隔
    // setInterval(yAxis, series) {
    //   let types = new Set()
    //   series.forEach((el) => {
    //     types.add(el.type)
    //   })
    //   types = Array.from(types)
    //   const maxArray = []
    //   const minArray = []
    //   types.forEach((m) => {
    //     let data = []
    //     series.forEach((n) => {
    //       if (m === n.type) {
    //         data = [...data, ...n.data]
    //       }
    //     })
    //     let max = Math.max(...data)
    //     const min = Math.min(...data)
    //     if (max > 0) {
    //       max = parseInt(Math.ceil(max))
    //     } else {
    //       max = parseInt(Math.floor(max))
    //     }
    //     if (max.toString().length === 1) {
    //       // 个位数
    //       if (max > 5) {
    //         max = 10
    //       }
    //       // else {
    //       //   max = 5
    //       // }
    //     } else if (max < 1) {
    //       max = 1
    //     } else {
    //       // 两位以上
    //       let first = ('' + max)[0]
    //       const second = ('' + max)[1]
    //       if (second > 5) {
    //         first = parseInt(first) + 1
    //         for (let i = 0; i < max.toString().length - 1; i++) {
    //           first = first + '0'
    //         }
    //       } else {
    //         first = first + '5'
    //         for (let i = 0; i < max.toString().length - 2; i++) {
    //           first = first + '0'
    //         }
    //       }
    //       max = parseInt(first)
    //     }
    //     maxArray.push(max)
    //     minArray.push(min)
    //   })
    //   yAxis.map((v, i) => {
    //     v.max = Math.ceil(maxArray[i]) // 向上取整
    //     v.min = Math.floor(minArray[i]) // 向下取整
    //     if (v.min > 0) {
    //       v.min = 0
    //     }
    //     if (v.max < 0) {
    //       v.max = 0
    //     }
    //     v.interval = (v.max - v.min) / 5
    //   })
    //   return yAxis
    // }
  }
}
</script>

2.2 封装饼图
<!--饼图-->
<template>
  <div :id="domId" :key="domId" :style="{ height: height, width: '100%',backgroundColor:'#fff' }" />
</template>
<script>
import * as echarts from 'echarts'
import resize from './mixins/resize'
import 'echarts/lib/component/dataZoom'

export default {
  mixins: [resize],
  props: {
    domId: { type: String, default: 'chartId' },
    height: { type: String, default: '300px' },
    data: { type: Object, default: null }
  },
  watch: {
    data: {
      handler(val) {
        // 这里使用nextTick解决Error: Initialize failed: invalid dom
        this.$nextTick(() => {
          this.initChart(val)
        })
      },
      deep: true
    }
  },
  mounted() {
    this.initChart(this.data)
  },
  methods: {
    initChart(data) {
      if (!data) {
        return false
      }
      const that = this
      this['myChart' + this.domId] = echarts.init(
        document.getElementById(this.domId)
      )
      this['myChart' + this.domId].setOption({
        grid: {
          left: '10%',
          top: '15%',
          right: '10%',
          bottom: '25%'
        },
        toolbox: data.toolbox,
        title: {
          text: data.title ? data.title : null,
          textStyle: {
            fontSize: 10,
            color: '#666666'
          },
          left: '5%'
        },
        legend: data.legend ? data.legend : {
          data: data.legendData,
          top: 0,
          textStyle: {
            fontSize: 9,
            color: '#666666'
          },
          formatter: (name) => {
            if (!name) return ''
            return that.getEqualNewlineString(name, 10) // 根据需求修改参数
          }
        },
        tooltip: data.tooltip ? data.tooltip : {
          trigger: 'axis'
        },
        series: data.series
      })
    },
    /**
     * 超出换行的方法
     * @param {String} params 要处理的字符串
     * @param {Number} length 每行显示长度
     * @returns {String}
    */
    getEqualNewlineString(params, length) {
      let text = ''
      const count = Math.ceil(params.length / length) // 向上取整数
      // 一行展示length个
      if (count > 1) {
        for (let z = 1; z <= count; z++) {
          text += params.substr((z - 1) * length, length)
          if (z < count) {
            text += '\n'
          }
        }
      } else {
        text += params.substr(0, length)
      }
      return text
    }
  }
}
</script>

2.3柱状/折线 一体图(2网格)
<!--柱状/折线 一体图(2grid)-->
<template>
  <div :id="domId" :key="domId" :style="{ height: height, width: '100%',backgroundColor:'#fff' }" />
</template>
<script>
import * as echarts from 'echarts'
import resize from './mixins/resize'
import 'echarts/lib/component/dataZoom'

export default {
  mixins: [resize],
  props: {
    domId: { type: String, default: 'chartId' },
    height: { type: String, default: '300px' },
    data: { type: Object, default: null }
  },
  watch: {
    data: {
      handler(val) {
        // 这里使用nextTick解决Error: Initialize failed: invalid dom
        this.$nextTick(() => {
          this.initChart(val)
        })
      },
      deep: true
    }
  },
  mounted() {
    this.initChart(this.data)
  },
  methods: {
    initChart(data) {
      if (!data) {
        return false
      }
      this['myChart' + this.domId] = echarts.init(
        document.getElementById(this.domId)
      )

      this['myChart' + this.domId].clear() // 清空画布,防止缓存

      this['myChart' + this.domId].setOption({
        grid: data.grid
          ? data.grid
          : [
            {
              left: '10%',
              top: '15%',
              right: '10%',
              height: '35%'
            },
            {
              left: '10%',
              top: '50%',
              right: '10%',
              height: '35%'
            }
          ],
        toolbox: data.toolbox,
        title: {
          text: data.title ? data.title : null,
          textStyle: {
            fontSize: 10,
            color: '#666666'
          },
          left: '5%'
        },
        legend: data.legend
          ? data.legend
          : {
            data: data.legendData,
            top: 0,
            textStyle: {
              fontSize: 9,
              color: '#666666'
            },
            formatter: function(name) {
              return name.length > 12 ? name.substr(0, 12) + '...' : name
            }
          },
        tooltip: data.tooltip
          ? data.tooltip
          : {
            trigger: 'axis'
          },
        xAxis: data.xAxis,
        yAxis: data.yAxis,
        dataZoom: data.dataZoom,
        series: data.series
      })
    }
  }
}
</script>

3 ECharts配置整理

例子参考

热力图参考例子:https://www.isqqw.com/viewer?id=31895

x轴 / y轴
xAxis: {
	show: true, // 是否显示x轴
	type: 'category', // 坐标轴类型,值category(类目轴)/value(数值轴),与y轴呼应,若x轴配置category则y轴配置value
	inverse: false, // 是否是反向坐标轴
	boundaryGap: ['20%', '20%'],    // 坐标轴两边留白策略,也可以使用布尔值,默认true居中
    axisLine: {
       show: true,    // 是否显示坐标轴轴线
    },
    axisTick: {
       show: true,    // 是否显示坐标轴刻度
       alignWithLabel: true,   //设置x轴刻度线与x轴文字对齐的
    },
    axisLabel: {
        show: true,     // 是否显示刻度标签
        interval: '0',    // 坐标轴刻度标签的显示间隔,在类目轴中有效.0显示所有
        rotate: 90,   // 刻度标签旋转的角度,在类目轴的类目标签显示不下的时候可以通过旋转防止标签之间重叠;旋转的角度从-90度到90度
        margin: 10,    // 刻度标签与轴线之间的距离
        // formatter 刻度标签的内容格式器,支持字符串模板和回调函数两种形式
        formatter: function (params) {
             // 横坐标超长处理
             let newParamsName = ''
             const paramsNameNumber = params.length
             const provideNumber = 15
             const rowNumber = Math.ceil(paramsNameNumber / provideNumber)
             if (paramsNameNumber > provideNumber) {
               for (let p = 0; p < rowNumber; p++) {
                 let tempStr = ''
                 const start = p * provideNumber
                 const end = start + provideNumber
                 if (p == rowNumber - 1) {
                   tempStr = params.substring(start, paramsNameNumber)
                 } else {
                   tempStr = params.substring(start, end) + '\n'
                 }
                 newParamsName += tempStr
               }
             } else {
               newParamsName = params
             }
             return newParamsName
         },
        color: '#FFF',     // 刻度标签文字的颜色
        fontStyle: 'normal',    // 字体的风格(normal无样式;italic斜体;oblique倾斜字体) 
        // 字体的粗细(normal无样式;bold加粗;bolder加粗再加粗;lighter变细;数字定义粗细也可以取值范围100至700)
        fontWeight: 'normal',    
        fontSize: '20',    // 文字字体大小
        align: 'left',     // 文字水平对齐方式,默认自动(left/center/right)
        verticalAlign: 'left',    // 文字垂直对齐方式,默认自动(top/middle/bottom)
        lineHeight: '50',    // 行高
        backgroundColor: 'red', // 文字块背景色,例:#123234, red, rgba(0,23,11,0.3)
    },
    splitLine: {
        show: false // 是否显示网格线
    }
},
文章来源:https://blog.csdn.net/Xxxxxl17/article/details/135112347
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。