Echarts使用记录

446 阅读11分钟

前言

Echartss是个人认为完成图表看板开发的第一把好手,拥有丰富的示例以及详细的API文档。介于API文档内容过多,因此对于一些主要的API进行解释以及记录在开发中所遇到的一些开发问题的处理方式。

主要组成部分(7个api)

  1. grid: 用于调整图表内容对于上下左右的距离(注意不是指整个图表)
  2. legend:针对图示的一系列配置,作用:对柱状图或者折线等等的解释以及控制每个部分的显隐
  3. tooltip:对鼠标经过图表内容显示浮窗显示的配置
  4. xAxis:对X轴的配置
  5. yAxis:对Y轴的配置
  6. dataZoom:对于图表内容的显示范围进行控制(实现滑动、缩放)
  7. series:用于图表内容具体数据的赋值以及一系列配置

以上是个人认为比较重要的几个配置,具体每个API的具体使用可在官网进行搜索查询

官网地址

echarts.apache.org/zh/index.ht…

开发要点记录

下列代码是基于TS进行编写

1. 实现双轴刻度间隔保持一致(针对yAxis进行配置)

问题场景: 实现柱状-折线图结合,左边刻度显示完成数量,右边刻度显示完成率,为了美观,要求左右刻度间隔保持一致。

解决思路: 根据实际数据计算完成数量的最大值以及完成率的最大值,并赋值在yAxis中max值;随后计算出相应的间隔数值(看个人需求,需要分割几份就按几份来进行计算),并赋值给在yAxis中interval值。

// 变量说明: textOptions 是用于Echarts初始化的渲染数据
 let totalArr = [10,30,25,32,55]
 // 计算最大值 55
 let max = Math.max.apply(null, totalArr) 
 // 因为我这里是按照5个间隔进行分割的,习惯向上取最大值的5的倍数,也方便下面计算间隔数
 let max5 = Math.ceil(max / 5) * 5 ?? 500 
 // 赋值最大值
 textOptions.yAxis[0].max = max5
 // 赋值间隔 =》 Math.ceil(max5 / 5) 计算最大值分割成5份,每份的值是多少
 Math.ceil(max5 / 5).yAxis[0].interval = Math.ceil(max5 / 5)

2. 采用..进行X轴刻度替换

问题场景: 当X轴刻度字数量太多,造成刻度之间进行挤压,重叠,看起来特别不美观

解决思路: 进行判断,当刻度长度大于多少时(具体长度需要看实际情况,如宽度是全屏的,建议10长度,款段是50%,建议6长度),采用..进行替换。(这里结合图表滑动dataZoom可以很好的处理X轴刻度问题)

//变量说明: textOptions 是用于Echarts初始化的渲染数据
let textOptions = {
  xAxis: {
    data: [] as string[],
    axisLabel: {
      color: '#fff',
      fontSize: 18,
      formatter: function (value: string) {
        return value.length > 6 ? value.slice(0, 4) + '..' : value // 保证长度保持在6长度
      }
    }
  }
}

3. 实现图表内容左右拖动效果

问题场景: 当数据内容过多,无法进行展示全部内容。

解决思路: 设置自定义显示范围,可以通过左右拖动内容或者鼠标滚动进行滑动

文档地址 echarts.apache.org/zh/option.h…

// 变量说明: textOptions 是用于Echarts初始化的渲染数据
let textOptions = {
  dataZoom: [
    {
      type: 'inside', // 滑动条型数据区域缩放
      start: 0, // 左侧在数据窗口范围的起始百分比, 0 表示从头开始
      end: 100, // 右侧在数据窗口范围的结束百分比, 100 表示到尾部结束
      moveOnMouseMove: true, // 表示不按任何功能键,鼠标移动能触发数据窗口平移
      zoomLock: false,
      show: false // 不显示滑块
    }
  ]
}
// 这里需要动态设置end的值,才能对数据进行动态显示
// XArr.length 坐标轴数据的总长度
// (5 / XArr.length) * 100 =》 页面只展示5个坐标轴,进行计算占总坐标长度的比例,
// 意味着不管数据多少,一进页面都只展示5条

    textOptions.dataZoom[0].end = (5 / XArr.length) * 100
   

4. 浏览器窗口大小变化时,图表大小跟随动态变更

问题场景: 当浏览器窗口切换为全屏时,图表随着进行变化

解决思路: 通过增加监听浏览器窗口变化事件,调用echartsAPI-resize进行图表重新渲染

// html部分
<div ref="chartRef" id="chartMain" /> // 用于挂载eharts图形的容器(该容器的高度需要进行动态高度计算)

// ts部分
let myChart = ref<any>(null) // 定义ref

// 在onMounted生命周期函数进行事件绑定

onMounted(() => {
   myChart = echarts.init(document.getElementById('chartMain') as any) // 图表初始化
   //echarts图表跟随屏幕的大小变化而变化
     window.addEventListener('resize', function () {
        myChart.resize()
    })
})

5. 图表点击事件

问题场景: 当需要给X轴标签或者柱状图增加点击事件

解决思路: 绑定点击事件,根据返回的params参数进行逻辑判断

// html部分
<div ref="chartRef" id="chartMain" /> // 用于挂载eharts图形的容器(该容器的高度需要进行动态高度计算)

// ts部分
let myChart = ref<any>(null) // 定义ref

// 在onMounted生命周期函数进行点击事件绑定
// params 包含了点击事件的相关信息
 myChart.on('click', function (params: any) {
      // params.value  ---> 所点击的值 (X轴标签事件)
      // params.componentSubType ---> 所点击的类型 bar(柱状图) line(折线) pie(饼图) (柱状图点击事件)
}

6. 重合与堆叠柱状图实现

问题场景: 当需要进行柱状图重合或堆叠

解决思路:

  1. 柱状图重合:设置需要重合的柱状图数据series中的barGap为-100%
  2. 柱状图堆叠:给每个需要堆叠在一起的柱状图数据series都增加stack字段,并设置为相同的值(字符串)

image.png

// 柱状图重合
let textOptions = {
  series: [
    {
      name:'name1',
      type: 'bar',
      data: [] as any[],
      color: '#fff',
      barGap: '-100%', // 该条柱状图为重叠柱状图
      emphasis: {
        disabled: true // 这里需要注意把经过柱状图高亮效果去除,不然会造成下层柱状图浮现上来的现象
      }
    },
    {
      name:'name2',
      type: 'bar',
      data: [] as any[],
      color: '#fff',
      emphasis: {
        disabled: true
      }
    },
  ]
}

// 柱状图堆叠:堆叠时需要注意Y周的最大刻度是按每个柱状图进行相加来计算的
let textOptions = {
  series: [
    {
      name:'name1',
      type: 'bar',
      stack: 'value',
      data: [] as any[],
      color: '#fff',
      emphasis: {
        disabled: true
      }
    },
    {
      name:'name2',
      type: 'bar',
      stack: 'value',
      data: [] as any[],
      color: '#fff',
      emphasis: {
        disabled: true
      }
    },
  ]
}

7. 横向柱状图实现

问题场景: 需要展示横向柱状图时

解决思路: 对xAxis设置type为value、yAxis设置type为category

image.png

// 横向柱状图-即是将yAxis设置当成X轴来配置,将xAxis当成Y轴来配置
let textOptions = {
  yAxis: {
    type: 'category',
    data: [] as any[],
    inverse: true, // 这个需要设置为true,不然数据是从下往上渲染的,视觉上会很奇怪
    // 设置坐标轴
    axisLabel: {
      interval: 0,
      color: '#000',
      fontSize: 18,
      fontWeight: 'bold', // 设置标题字体为加粗
      formatter: function (value: string) {
        return value.length > 6 ? value.slice(0, 4) + '..' : value
      }
    },
    axisTick: {
      show: false // 隐藏Y轴刻度线
    },
    axisLine: {
      show: false // 不显示坐标轴线
    }
  },
  xAxis: {
    data: [] as any[],
    type: 'value',
    // 设置value展示
    axisLabel: {
      interval: 0,
      color: '#000',
      fontSize: 18
    },
    splitLine: {
      //网格线
      show: false,
      lineStyle: {
        color: '#62697c'
      },
      type: 'dashed'
    },
    min: 0, // 最大值
    max: 500, // 最小值
    interval: 0
  }
}

8. 堆叠横向柱状图-平均划分(案例)

问题场景: 当每个模块的数据相差过大时(即每个模块的最大值相差过大),由于每个模块是公用最大值的,当进行堆叠的时候,数值较小的模块的柱状图份额会比较小,导致不美观以及数据显示不全

解决思路: 通过计算出每个模块中堆叠的柱状图数据占当前模块最大值的比例是多少,再计算出这个比例X公用最大值的数作为数据赋值到柱状图中,然后通过修改标签,将原本的数值进行显示。这样做有个缺点:就是每个模块的柱状图长度都是一样的,不能很明显的突出数据对比。

image.png

  let testOption =  {
        grid: {
          top: 0,
          bottom: 0,
          left: 105,
          right: 240
        },
        yAxis: {
          type: 'category',
          data: [] as any[],
          inverse: true, // 反转渲染
          // Y轴刻度样式修改
          axisLabel: {
            interval: 0,
            color: '#000',
            fontSize: 18,
            fontWeight: 'bold', // 设置标题字体为加粗
            formatter: function (value: string) {
              return value.length > 6 ? value.slice(0, 4) + '..' : value
            }
          },
          axisTick: {
            show: false // 隐藏Y轴刻度线
          },
          axisLine: {
            show: false // 不显示坐标轴线
          }
        },
        xAxis: {
          data: [] as any[],
          type: 'value',
          axisLabel: {
            interval: 0,
            color: '#000',
            fontSize: 18
          },
          splitLine: {
            //网格线
            show: false,
            lineStyle: {
              color: '#62697c'
            },
            type: 'dashed'
          },
          min: 0,
          max: 500,
          interval: 0
        },
        dataZoom: [
          {
            type: 'inside', // 使用滑动条形式的 dataZoom 组件
            yAxisIndex: 0,
            start: 0, // 左侧在数据窗口范围的起始百分比, 0 表示从头开始
            end: 100, // 右侧在数据窗口范围的结束百分比, 100 表示到尾部结束
            show: false,
            realtime: true
          }
        ],
        series: [
          {
            name: 'value1',
            type: 'bar',
            barWidth: '65%',
            stack: 'status', // 堆叠
            data: [] as any[],
            // 设置为圆角矩形
            shape: 'roundRect', 
            itemStyle: {
              color: '#0ecad2',
              borderRadius: [10, 10, 10, 10] // 设置圆角的半径
            },
            // 数值显示
            label: {
              show: true, 
              position: 'inside', 
              fontSize: 18,
              color: '#fff',
              fontWeight: 'bold', 
              formatter: function (params: any) {
                return params.value === 0 ? '' : params.value
              }
            },
            // 关闭高亮
            emphasis: {
              disabled: true
            }
          },
        ..... // 需要堆叠几个柱状图就写几个,这里不一一进行展示了
        ]
      },
 // 数据处理     
let getData = () => {
   // 这里是每个柱子的数值(真实数值)
  let valueArr1: any[] = [1000, 1000, 1000, 100, 10] 
  let valueArr2: any[] = [20, 20, 20, 20, 20] 
  let valueArr3: any[] = [30, 30, 30, 30, 30] 
  let valueArr4: any[] = [40, 40, 40, 40, 40] 
  let valueArr5: any[] = [50, 50, 50, 50, 50] 
  let valueArr6: any[] = [60, 60, 60, 60, 60]
  let sumArr: any[] =[1200,1200,1200,300,210] // 总数
 
 //获取最大值
  let max = Math.max.apply(null, sumArr)
  
 // 通过比例计算结合最大值获取数据匹配值,用作图表展示
  let valueArrShow1 = valueArr1.map((item, index) => {
    return max * (item / sumArr[index])
  }) 
  let valueArrShow2 = valueArr2.map((item, index) => {
    return max * (item / sumArr[index])
  })
  let valueArrShow3 = valueArr3.map((item, index) => {
    return max * (item / sumArr[index])
  })
  let valueArrShow4 = valueArr4.map((item, index) => {
    return max * (item / sumArr[index])
  })
  let valueArrShow5 = valueArr5.map((item, index) => {
    return max * (item / sumArr[index])
  })
  let valueArrShow6 = valueArr6.map((item, index) => {
    return max * (item / sumArr[index])
  })
  // 赋值最大值
  testOption.xAxis.max = max
  // 赋值图表
  testOption.series[0].data = valueArrShow1
  // 匹配实际值进行展示
  testOption.series[0].label.formatter = function (params: any) {
    return valueArr1[params.dataIndex] ?? ''
  }
 ......
}

9. 饼图-标签合并显示

问题场景: 当其中两个饼块需要相加公用一条label引导线进行展示

解决思路:

  1. 在原本的模块数据中:增加一个极小数值的模块,然后通过自定义label设置,展示两个模块相加的数值
  2. 隐藏正常模块数据的引导线,只展示刚刚增加的极小数值的模块
  3. 主要思路:在赋值给series中data值时,可以将对应配置一起写进去,从而能控制引导线的动态显示

image.png

const  ECOption = {
      // tip提示框设置
      tooltip: {
          trigger: 'item',
          formatter: function (params: any) {
            // params 是包含数据信息的对象
            return `正向数量:99 </br> EH数量:1</br> 合计:100`
          } as any,
          textStyle: {
            fontWeight: 'bold'
          },
          axisPointer: {
            type: 'none'
          }
        },
        // 标题 (设置在中间)
        title: {
          text: `{a| 70% }`,
          textStyle: {
            rich: {
              a: {
                fontSize: 20,
                color: '#ff0000',
                fontWeight: 'bold' // 设置标题字体为加粗
              }
            }
          },
          subtext: `{a| 达成}`,
          subtextStyle: {
            rich: {
              a: {
                fontSize: 17,
                color: '#0202ff',
                padding: [0, 0, 0, 7],
                fontWeight: 'bold' // 设置标题字体为加粗
              }
            }
          },
          x: '38.8%',
          y: '50%'
        },
        // 数据
        series: [
          {
            type: 'pie',
            radius: ['35%', '66%'], // 外圈大小
            center: ['45%', '58%'], // 中心部分
            left: 0,
            top: 0,
            bottom: 0,
            labelLine: {
              normal: {
                show: true,
                length: 5,
                length2: 10
              }
            },
            // 引导线设置
            label: {
              formatter: '{c}\n',
              borderWidth: 15,
              borderRadius: 8,
              padding: [0, -35],
              textStyle: {
                fontWeight: 'bold',
                fontSize: 18
              }
            },
            data: [] as any[],
            // 饼块颜色设置-这里需要设置6个颜色
            itemStyle: {
               normal: {
               color: function (colors: any) {
          let colorList = ['#21ea89', '#05bc58', '#05bc58', '#ff4267', '#d70c32', '#d70c32']
               return colorList[colors.dataIndex]
                }
              }
            }
          }
        ]
      }
      
      
      // 数据匹配-在data设置引导线的show值,可以控制引导线的动态显示与隐藏
     let getMaterialPie = async () => {
        let data = [
          {
            value: 100,
            name: 'value1',
            label: {
              show: (function () {
                return res.data.notEHTotal ? true : false
              })(),
              formatter: '{c}',
              position: 'inside',
              textStyle: {
                fontWeight: 'bold',
                fontSize: 15,
                color: '#000'
              }
            },
            labelLine: {
              show: false
            }
          },
          // 这里赋值一个0.001 只是用来展示相加的值
          {
            value: 0.001,
            name: 'value2',
            label: {
              show: true,
              formatter: function (params: any) {
                // params 是包含数据信息的对象
                // return `${res.data.notEHTotal + res.data.ehTotal}\n`
                return `199`
              },
              borderWidth: 15,
              borderRadius: 8,
              padding: [0, -50],
              textStyle: {
                fontWeight: 'bold',
                fontSize: 15,
                color: '#000'
              }
            },
            labelLine: {
              show: true,
              length: 20,
              length2: 90
            }
          },
          {
            value: 99,
            name: 'value3',
            label: {
              show: (function () {
                return res.data.ehTotal ? true : false
              })(),
              formatter: '{c}',
              position: 'inside',
              textStyle: {
                fontWeight: 'bold',
                fontSize: 15,
                color: '#000'
              }
            },
            labelLine: {
              show: false
            }
          },
          {
            value: 100,
            // value: 222,
            name: 'value4',
            label: {
              show: (function () {
                return res.data.notEHUndercount ? true : false
              })(),
              formatter: '{c}',
              position: 'inside',
              textStyle: {
                fontWeight: 'bold',
                fontSize: 15,
                color: '#000'
              }
            },
            labelLine: {
              show: false
            }
          },
          // 这里赋值一个0.001 只是用来展示相加的值
          {
            value: 0.001,
            name: 'value5',
            label: {
              show: true,
              formatter: function (params: any) {
                // params 是包含数据信息的对象
                // return `${res.data.notEHUndercount + res.data.ehUndercount}\n`
                return `199`
              },
              borderWidth: 15,
              borderRadius: 8,
              padding: [0, -50],
              textStyle: {
                fontWeight: 'bold',
                fontSize: 15,
                color: '#000'
              }
            },
            labelLine: {
              show: true,
              length: 20,
              length2: 90
            }
          },
          {
            value: 99,
            name: 'value6',
            label: {
              show: (function () {
                return res.data.ehUndercount ? true : false
              })(),
              formatter: '{c}',
              position: 'inside',
              textStyle: {
                fontWeight: 'bold',
                fontSize: 15,
                color: '#000'
              }
            },
            labelLine: {
              show: false
            }
          }
        ]
        
        ECOption.series[0].data = data
        myChartPie.setOption(dataMap.ECOption)
      
    }
      

10. 横向柱状图-增加虚线效果

问题场景: 在柱状图旁边增加虚线

解决思路: 将虚线看成当成一条细小的柱状图进行调整,将其修改为虚线状态,并移动位置

image.png