ECharts中多图表联动且主图表增加选中时的高亮效果

        在实际开发中,当遇到两个图表联动,主表为柱状图,副表为线性图;当选中主表某地区时,需要将此地区进行高亮处理,并更新副表数据;此时,主表中须使用到splitArea属性,给选中区域添加背景色,表示该区域已被选中。效果如下图:

        如图,我们此次完成是指定区域的电量发电量以及使用率等数据展示,并且当主表切换地区时,更新区域的高亮样式,以及实时更新副表中使用率数据。

一、测试数据

        以下为本次图表效果测试数据,代码如下:

[
  {
    "name": "北京",
    "online": [
      {
        "name": 1,
        "value": 70
      },
      {
        "name": 2,
        "value": 20
      },
      {
        "name": 3,
        "value": 24
      },
      {
        "name": 4,
        "value": 13
      },
      {
        "name": 5,
        "value": 14
      },
      {
        "name": 6,
        "value": 12
      },
      {
        "name": 7,
        "value": 14
      },
      {
        "name": 8,
        "value": 68
      },
      {
        "name": 9,
        "value": 51
      },
      {
        "name": 10,
        "value": 24
      },
      {
        "name": 11,
        "value": 70
      },
      {
        "name": 12,
        "value": 76
      }
    ],
    "onlineValue": 4560,
    "electric": [
      {
        "name": 1,
        "value": 93
      },
      {
        "name": 2,
        "value": 13
      },
      {
        "name": 3,
        "value": 20
      },
      {
        "name": 4,
        "value": 27
      },
      {
        "name": 5,
        "value": 22
      },
      {
        "name": 6,
        "value": 11
      },
      {
        "name": 7,
        "value": 26
      },
      {
        "name": 8,
        "value": 63
      },
      {
        "name": 9,
        "value": 54
      },
      {
        "name": 10,
        "value": 23
      },
      {
        "name": 11,
        "value": 85
      },
      {
        "name": 12,
        "value": 58
      }
    ],
    "electricValue": 4950
  },
  {
    "name": "天津",
    "online": [
      {
        "name": 1,
        "value": 58
      },
      {
        "name": 2,
        "value": 27
      },
      {
        "name": 3,
        "value": 16
      },
      {
        "name": 4,
        "value": 14
      },
      {
        "name": 5,
        "value": 30
      },
      {
        "name": 6,
        "value": 11
      },
      {
        "name": 7,
        "value": 19
      },
      {
        "name": 8,
        "value": 57
      },
      {
        "name": 9,
        "value": 86
      },
      {
        "name": 10,
        "value": 21
      },
      {
        "name": 11,
        "value": 72
      },
      {
        "name": 12,
        "value": 61
      }
    ],
    "onlineValue": 4720,
    "electric": [
      {
        "name": 1,
        "value": 71
      },
      {
        "name": 2,
        "value": 23
      },
      {
        "name": 3,
        "value": 15
      },
      {
        "name": 4,
        "value": 20
      },
      {
        "name": 5,
        "value": 28
      },
      {
        "name": 6,
        "value": 20
      },
      {
        "name": 7,
        "value": 20
      },
      {
        "name": 8,
        "value": 95
      },
      {
        "name": 9,
        "value": 85
      },
      {
        "name": 10,
        "value": 29
      },
      {
        "name": 11,
        "value": 73
      },
      {
        "name": 12,
        "value": 80
      }
    ],
    "electricValue": 5590
  },
  {
    "name": "上海",
    "online": [
      {
        "name": 1,
        "value": 96
      },
      {
        "name": 2,
        "value": 20
      },
      {
        "name": 3,
        "value": 26
      },
      {
        "name": 4,
        "value": 21
      },
      {
        "name": 5,
        "value": 20
      },
      {
        "name": 6,
        "value": 28
      },
      {
        "name": 7,
        "value": 24
      },
      {
        "name": 8,
        "value": 51
      },
      {
        "name": 9,
        "value": 95
      },
      {
        "name": 10,
        "value": 13
      },
      {
        "name": 11,
        "value": 68
      },
      {
        "name": 12,
        "value": 59
      }
    ],
    "onlineValue": 5210,
    "electric": [
      {
        "name": 1,
        "value": 98
      },
      {
        "name": 2,
        "value": 30
      },
      {
        "name": 3,
        "value": 15
      },
      {
        "name": 4,
        "value": 29
      },
      {
        "name": 5,
        "value": 28
      },
      {
        "name": 6,
        "value": 15
      },
      {
        "name": 7,
        "value": 25
      },
      {
        "name": 8,
        "value": 88
      },
      {
        "name": 9,
        "value": 98
      },
      {
        "name": 10,
        "value": 29
      },
      {
        "name": 11,
        "value": 66
      },
      {
        "name": 12,
        "value": 69
      }
    ],
    "electricValue": 5900
  },
  {
    "name": "重庆",
    "online": [
      {
        "name": 1,
        "value": 77
      },
      {
        "name": 2,
        "value": 19
      },
      {
        "name": 3,
        "value": 29
      },
      {
        "name": 4,
        "value": 30
      },
      {
        "name": 5,
        "value": 24
      },
      {
        "name": 6,
        "value": 21
      },
      {
        "name": 7,
        "value": 25
      },
      {
        "name": 8,
        "value": 62
      },
      {
        "name": 9,
        "value": 72
      },
      {
        "name": 10,
        "value": 19
      },
      {
        "name": 11,
        "value": 89
      },
      {
        "name": 12,
        "value": 65
      }
    ],
    "onlineValue": 5320,
    "electric": [
      {
        "name": 1,
        "value": 71
      },
      {
        "name": 2,
        "value": 10
      },
      {
        "name": 3,
        "value": 28
      },
      {
        "name": 4,
        "value": 14
      },
      {
        "name": 5,
        "value": 14
      },
      {
        "name": 6,
        "value": 22
      },
      {
        "name": 7,
        "value": 17
      },
      {
        "name": 8,
        "value": 81
      },
      {
        "name": 9,
        "value": 71
      },
      {
        "name": 10,
        "value": 12
      },
      {
        "name": 11,
        "value": 76
      },
      {
        "name": 12,
        "value": 72
      }
    ],
    "electricValue": 4880
  },
  {
    "name": "南京",
    "online": [
      {
        "name": 1,
        "value": 99
      },
      {
        "name": 2,
        "value": 12
      },
      {
        "name": 3,
        "value": 28
      },
      {
        "name": 4,
        "value": 30
      },
      {
        "name": 5,
        "value": 13
      },
      {
        "name": 6,
        "value": 29
      },
      {
        "name": 7,
        "value": 19
      },
      {
        "name": 8,
        "value": 70
      },
      {
        "name": 9,
        "value": 82
      },
      {
        "name": 10,
        "value": 26
      },
      {
        "name": 11,
        "value": 50
      },
      {
        "name": 12,
        "value": 56
      }
    ],
    "onlineValue": 5140,
    "electric": [
      {
        "name": 1,
        "value": 75
      },
      {
        "name": 2,
        "value": 11
      },
      {
        "name": 3,
        "value": 23
      },
      {
        "name": 4,
        "value": 25
      },
      {
        "name": 5,
        "value": 11
      },
      {
        "name": 6,
        "value": 24
      },
      {
        "name": 7,
        "value": 13
      },
      {
        "name": 8,
        "value": 72
      },
      {
        "name": 9,
        "value": 69
      },
      {
        "name": 10,
        "value": 28
      },
      {
        "name": 11,
        "value": 63
      },
      {
        "name": 12,
        "value": 67
      }
    ],
    "electricValue": 4810
  },
  {
    "name": "西安",
    "online": [
      {
        "name": 1,
        "value": 50
      },
      {
        "name": 2,
        "value": 17
      },
      {
        "name": 3,
        "value": 17
      },
      {
        "name": 4,
        "value": 11
      },
      {
        "name": 5,
        "value": 15
      },
      {
        "name": 6,
        "value": 15
      },
      {
        "name": 7,
        "value": 22
      },
      {
        "name": 8,
        "value": 93
      },
      {
        "name": 9,
        "value": 92
      },
      {
        "name": 10,
        "value": 27
      },
      {
        "name": 11,
        "value": 57
      },
      {
        "name": 12,
        "value": 89
      }
    ],
    "onlineValue": 5050,
    "electric": [
      {
        "name": 1,
        "value": 76
      },
      {
        "name": 2,
        "value": 19
      },
      {
        "name": 3,
        "value": 12
      },
      {
        "name": 4,
        "value": 19
      },
      {
        "name": 5,
        "value": 14
      },
      {
        "name": 6,
        "value": 30
      },
      {
        "name": 7,
        "value": 11
      },
      {
        "name": 8,
        "value": 53
      },
      {
        "name": 9,
        "value": 70
      },
      {
        "name": 10,
        "value": 19
      },
      {
        "name": 11,
        "value": 74
      },
      {
        "name": 12,
        "value": 95
      }
    ],
    "electricValue": 4920
  }
]

        单独存放在一个js文件中,使用时在页面中引入即可。

二、创建页面

        首先,在Vue项目中,创建一个模板,在路由器中添加该模板为一个新页面,并通过路由访问该页面。代码如下:

<template>
  <div class="chart" ref="chart"></div>
</template>
<script>
import { } from './data/options'
export default {
  name: 'SplitBg',
  data () {
    return {
      chartRef: null
    }
  },
  mounted () {
    this.initialChart()
  },
  methods: {
    initialChart () {
      // 初始化画板
      this.chartRef = this.$echart.init(this.$refs['chart'])
    },
    // 渲染图表
    renderChart () {

    }
  }
}
</script>
<style lang="less" scoped>
.chart{
  width: 100%;
  height: 100%;
}
</style>

        此时图表的画板已创建,但页面没有任何数据信息,这时则需要添加图表配置项等相关数据。

三、创建主副图表

        我们先将主副图表创建出来,再慢慢完善配置项,并达到预期效果。

        创建options.js文件,将“一、测试数据”也一并保存在该js文件中,通过import导入到页面中。options.js代码如下:


export const chartData = [
  {
    "name": "北京",
    "online": [
      {
        "name": 1,
        "value": 70
      },
      {
        "name": 2,
        "value": 20
      },
      {
        "name": 3,
        "value": 24
      },
      {
        "name": 4,
        "value": 13
      },
      {
        "name": 5,
        "value": 14
      },
      {
        "name": 6,
        "value": 12
      },
      {
        "name": 7,
        "value": 14
      },
      {
        "name": 8,
        "value": 68
      },
      {
        "name": 9,
        "value": 51
      },
      {
        "name": 10,
        "value": 24
      },
      {
        "name": 11,
        "value": 70
      },
      {
        "name": 12,
        "value": 76
      }
    ],
    "onlineValue": 4560,
    "electric": [
      {
        "name": 1,
        "value": 93
      },
      {
        "name": 2,
        "value": 13
      },
      {
        "name": 3,
        "value": 20
      },
      {
        "name": 4,
        "value": 27
      },
      {
        "name": 5,
        "value": 22
      },
      {
        "name": 6,
        "value": 11
      },
      {
        "name": 7,
        "value": 26
      },
      {
        "name": 8,
        "value": 63
      },
      {
        "name": 9,
        "value": 54
      },
      {
        "name": 10,
        "value": 23
      },
      {
        "name": 11,
        "value": 85
      },
      {
        "name": 12,
        "value": 58
      }
    ],
    "electricValue": 4950
  },
  
  // 略...
]

export const chartOptions = (chartData, selectedIndex) => {
  return {}
}

页面代码如下:

<template>
  <div class="chart" ref="chart"></div>
</template>
<script>
import { chartData, chartOptions } from './data/options'
export default {
  name: 'SplitBg',
  data () {
    return {
      chartRef: null,
      selectedIndex: 0
    }
  },
  mounted () {
    this.initialChart()
  },
  methods: {
    initialChart () {
      // 初始化画板
      this.chartRef = this.$echart.init(this.$refs['chart'])
      this.renderChart()
    },
    // 渲染图表
    renderChart () {
      this.chartRef.setOption(chartOptions(chartData, selectedIndex))
    }
  }
}
</script>
<style lang="less" scoped>
.chart{
  width: 100%;
  height: 100%;
}
</style>

3.1 区域划分

        先将主副表区域绘制出来,这部分代码也比较简单,配置项如下:

export const chartOptions = (chartData, selectedIndex) => {

  return {
    grid: [
      {
        bottom: '55%'
      },
      {
        top: '58%'
      }
    ],
    xAxis: [
      {
        type: 'category',
        data: chartData.map((item) => item.name),
        gridIndex: 0
      },
      {
        type: 'category',
        data: chartData[selectedIndex]['electric'].map((item) => item.name),
        gridIndex: 1
      }
    ],
    yAxis: [
      {
        type: 'value',
        gridIndex: 0,
        name: '单元:kWh'
      },
      {
        type: 'value',
        gridIndex: 1,
        name: '单位:%'
      }
    ]
  }
}

        页面效果如下图:

        在配置项中,首先是通过grid网格将画板分割成两部分,并且指定xAxis和yAxis中gridIndex索引,将坐标系绘制到对应的网格区域中。

        另外,如上图数据结构可见,副表中横坐标则是要通过主表中数据获取,所以在渲染时,默认先取第一项中的电量数量,即selectedIndex默认为0。

3.2 绘制图表

        接下来则是在配置项中添加数据,在配置项中增加series即可,并且告诉主副表,各自对应的xAxisIndex和yAxisIndex。柱状图都在坐标系0位置,线性图都在坐标系为1位置。

        代码如下:

export const chartOptions = (chartData, selectedIndex) => {
  // 电量图公共配置参数
  const barOption = {
    type: 'bar',
    barWidth: 20,
    yAxisIndex: 0,
    xAxisIndex: 0
  }
  // 线性图公共配置项
  const lineOption = {
    type: 'line',
    smooth: true,
    barWidth: 20,
    areaStyle: {
      opacity: 0.3
    },
    yAxisIndex: 1,
    xAxisIndex: 1
  }
  return {
    grid: [
      {
        bottom: '55%'
      },
      {
        top: '58%'
      }
    ],
    xAxis: [
      {
        type: 'category',
        data: chartData.map((item) => item.name),
        gridIndex: 0
      },
      {
        type: 'category',
        data: chartData[selectedIndex]['electric'].map((item) => item.name),
        gridIndex: 1
      }
    ],
    yAxis: [
      {
        type: 'value',
        gridIndex: 0,
        name: '单元:kWh'
      },
      {
        type: 'value',
        gridIndex: 1,
        name: '单位:%'
      }
    ],
    series: [
      {name: '上网电量', ...barOption, data: chartData.map(item => item.onlineValue)},
      {name: '发电电量', ...barOption, data: chartData.map(item => item.electricValue)},
      {
        name: '上网电量使用率',
        ...lineOption,
        data: chartData[selectedIndex]['online'].map((item) => item.value)
      },
      {
        name: '发电率',
        ...lineOption,
        data: chartData[selectedIndex]['electric'].map((item) => item.value)
      }
    ]
  }
}

        此时页面效果如下图:

3.3 样式调整

        如上图,图表看上去似乎不太美观,对分割线进行细调整即可。代码如下:

export const chartOptions = (chartData, selectedIndex) => {
  // 略...

  return {
    // 略...    

    yAxis: [
      {
        type: 'value',
        gridIndex: 0,
        name: '单元:kWh',
        splitLine: {
          show: true,
          lineStyle: {
            type: 'dashed',
            color: '#063f6b'
          }
        }
      },
      {
        type: 'value',
        gridIndex: 1,
        name: '单位:%',
        splitLine: {
          show: true,
          lineStyle: {
            type: 'dashed',
            color: '#063f6b'
          }
        }
      }
    ],

    // 略...
  }
}

        将分割线的颜色和线样式调整后,如下图:

3.4 图例组件

        给电量图和线性图,添加图例组件legend。如果直接在配置项中添加legend属性,则主副表的图例数据全部显示在一起。配置代码如下:

export const chartOptions = (chartData, selectedIndex) => {
  // 略...

  return {
    // 添加图例组件
    legend: {},
    
    // 略...

    series: [
      {name: '上网电量', ...barOption, data: chartData.map(item => item.onlineValue)},
      {name: '发电电量', ...barOption, data: chartData.map(item => item.electricValue)},
      {
        name: '上网电量使用率',
        ...lineOption,
        data: chartData[selectedIndex]['online'].map((item) => item.value)
      },
      {
        name: '发电率',
        ...lineOption,
        data: chartData[selectedIndex]['electric'].map((item) => item.value)
      }
    ]
  }
}

        页面效果如下图:

        此时,我们要的效果是,“上网电量”和“发电电量”图例显示在主图表上方,“上网电量使用率”和“发电率”图例显示在副图表上方。方法还是和xAxis和yAxis一样,将它们对象模式,改为数组模式即可,并且显示图例需要手动指定。配置代码如下:

export const chartOptions = (chartData, selectedIndex) => {
  // 略...

  return {
    legend: [
      {
        data: ['上网电量', '发电电量'],
        top: 10,
        textStyle: {
          color: '#FFF'
        }
      },
      {
        data: ['上网电量使用率', '发电率'],
        top: '51%',
        textStyle: {
          color: '#FFF'
        }
      }
    ],
    
    // 略...

    series: [
      {name: '上网电量', ...barOption, data: chartData.map(item => item.onlineValue)},
      {name: '发电电量', ...barOption, data: chartData.map(item => item.electricValue)},
      {
        name: '上网电量使用率',
        ...lineOption,
        data: chartData[selectedIndex]['online'].map((item) => item.value)
      },
      {
        name: '发电率',
        ...lineOption,
        data: chartData[selectedIndex]['electric'].map((item) => item.value)
      }
    ]
  }
}

        页面效果如下图:

3.5 添加区域选中高亮效果

        在主表中,添加选中区域高亮效果,通过splitArea修改区域背景色。配置项代码如下:

export const chartOptions = (chartData, selectedIndex) => {

  // 略...

  return {

    // 略...

    xAxis: [
      {
        type: 'category',
        data: chartData.map((item) => item.name),
        gridIndex: 0,
        splitArea: {
          show: true,
          areaStyle: {
            // 在官方文档中可见,此处Color为数组,分别对应X轴每个地区
            // 所以须循环对每个区域进行背景设置,并通过selectedIndex修改选中区域样式
            color: chartData.map(
                (_, index) => selectedIndex === index ? 
                    'rgba(29, 43, 104, .3)' : 'rgba(0, 0, 0, 0)'
            )
          }
        }
      },
      {
        type: 'category',
        data: chartData[selectedIndex]['electric'].map((item) => item.name),
        gridIndex: 1
      }
    ],
    
    // 略...
  }
}

        在xAxis横坐标系上,给选中区域添加背景色。效果如下图:

3.6 点击切换事件

        当高亮效果添加好后,则要增加主图表的点击功能,通过点不同地区,切换数据,实现联动功能。

        另外,对于一个坐标系区域点击触发事件,在之前一篇中已讲解,这里则直接使用,如不清楚的朋友,可以前去翻看一下。地址:ECharts的柱状图表中当数据量较少时不易触发点击事件使用getZr()方法来替换触发事件-CSDN博客

        在页面初始化图表位置,增加点击事件。代码如下:

initialChart () {
  // 初始化画板
  this.chartRef = this.$echart.init(this.$refs['chart'])
  // 点击事件
  this.chartRef.getZr().on('click', (e) => {
    // 定义当前坐标数组
    const pointPixel = [e.offsetX, e.offsetY]
    // 判断一个点是否在直角坐标系风格内,这里'grid'表示整个直角坐标系网格区域
    if (this.chartRef.containPixel('grid', pointPixel)) {
      const convertParam = this.chartRef.convertFromPixel({ seriesIndex: 0 }, pointPixel)
      // 如果返回为空,则不执行后续操作
      if (!convertParam) return
      // 定义横向坐标索引
      this.selectedIndex = parseInt(convertParam[0])
      // 重新渲染图表
      this.renderChart()
    }
  })
  this.renderChart()
}

        此时,点击各个地区,可以修改选中区域的高亮效果,并且使用率线性图表数据也能实时更新了。如下图:

        此篇就先讲到这了,希望对大家会有所帮助~