vue3+echarts 多Y轴折线图,通过图例legend动态控制对应Y轴所有数据显示隐藏

最近小编接到了一个新的需求,需要对echarts折线图页面进行优化

如图所示,上面的折线图,不同的变量之间值的差额过大,共用同一个Y轴时,折线趋势并不明显,比较影响用户的观看体验。

鉴于第一版的问题,升级了第二版,多Y轴折线图。这样哪怕不同的数据之间差距较大,但因为使用的是不同的坐标Y轴,彼此之间也不会互相影响,折线趋势也能一目了然。

但优化并没有止步于此,升级版的折线图,又提出了新的需求:当图例过多时,并不想查看那么多折线数据,需要在点击对应图例时,隐藏该图例所有的Y轴数据,具体效果如下:

别看这一个小小的需求,小编测验了好多次,花费了一些时间才解决。

最开始小编先用的模拟数据,创建了一个多Y轴图表,后来再加上legend图例控制Y轴显示功能,初步是实现了进阶需求(如上图)。但是在将页面替换成真实接口数据,写死的数据替换成动态生成的yAxis和series时,完成这个功能时,却一直存在bug,如下:

点击对应图例时,只隐藏了Y轴对应的刻度和数值label,但是Y轴轴线和名称仍在。

代码反反复复测试了很多次,也在网上看了一些其他up的参考意见,经过一番修改后,终于完成了需求;

css 复制代码
<template>
  <div class="PNMFlowChart">
    <div ref="chartRef" class="chart" ></div>
  </div>
</template>
 
<script setup>
import { getCurrentInstance, ref } from "vue";
import * as echarts from "echarts";
 
/** 数据初始化 */
const { proxy } = getCurrentInstance();
const chartOption = ref({});
const legendArr = ref([]);
let analysisChart = null;
const colors = [  "#1B9AEE", "#00AF75", "#B147B5", "#D37201", "#8FA008", "#F08080",];
 
const initChart = (data, name) => {
  let time = data.date || [];
  let title = name;
  legendArr.value = data?.list?.map(item => item.name) || [];
  let chartData = data?.list || [];
  let legendSelectedData = legendArr.value.reduce((acc, cur) => {
    acc[cur] = true; // 默认全部选中
    return acc;
  }, {});
 
  if (!analysisChart) {
    analysisChart = echarts.init(proxy.$refs.chartRef);
  } else {
    analysisChart.clear();
  }
 
  chartOption.value = {
    grid: {
      left: '10%',
      top: '10%',
      right: '10%',
      bottom: '10%',
      containLabel: true
    },
    legend: {
      type: "scroll",
      icon: "circle",
      data: legendArr.value,
      itemGap: 30,
      selected: legendSelectedData,
    },
    tooltip: {
      trigger: "axis",
      backgroundColor: "#FFFFFF",
      color: "#000000",
      borderWidth: 0,
      borderRadius: 0,
      axisPointer: {
        type: 'cross'
      }
    },
    toolbox: {
      feature: {
        saveAsImage: { title: '保存图表', name: title },
      },
      top: 5,
      right: 30
    },
    dataZoom: [
      {
        show: true,
        start: 0,
        end: 100,
        height: 20,
      }
    ],
    xAxis: {
      type: 'category',
      name: '时间',
      nameTextStyle: {
        padding: [0, -20, 0, 0],
      },
      nameLocation: 'start',
      nameGap: 40,
      data: time || [],
      axisTick: {
        alignWithLabel: true
      },
      axisLabel: {
        color: "#666666",
        fontSize: 12,
      },
      axisLine: {
        show: true,
        lineStyle: {
          color: "#666666",
          width: 1,
          type: "solid",
        },
      },
    },
    yAxis: (chartData.length > 0 ? chartData : [{}]).map((item = {}, index) => {
      const name = item.name || '数值';
      const unit = item.unit || ' ';
      let YColors = chartData.length > 0 ? colors[index % colors.length] : "#666666";
      const sideIndex = Math.floor(index / 2);
      const offset = sideIndex === 0 ? 0 : sideIndex * 70;
      return {
        type: 'value',
        name: `${name}/${unit}`,
        show: true, // 默认显示
        position: index % 2 === 0 ? 'left' : 'right',
        offset: offset,
        nameTextStyle: {
          color: '#666666',
          fontSize: 12,
          lineHeight: 40,
          opacity: 1,
        },
        splitLine: {
          lineStyle: {
            type: 'dashed',
            color: '#ccc'
          }
        },
        axisLine: {
          show: true,
          lineStyle: {
            color: YColors,
          }
        },
        axisTick: {
          show: true,
          length: 5,
          lineStyle: {
            width: 2,
            color: YColors,
            type: 'solid'
          }
        },
        axisLabel: {
          show: true,
          color: YColors,
          fontSize: 12,
          formatter: `{value}`
        },
        nameGap: 30,
      };
    }),
    series: chartData.map((item, index) => {
      return {
        name: item.name,
        type: "line",
        yAxisIndex: index,
        lineStyle: {
          width: 2,
          shadowColor: 'rgba(0,0,0,0.4)',
          shadowBlur: 5,
          shadowOffsetY: 5
        },
        itemStyle: {
          color: colors[index % colors.length],
        },
        data: item.data,
      };
    }),
  };
 
  // 设置初始配置
  analysisChart.setOption(chartOption.value);
 
    // 绑定图例点击事件,动态控制Y轴显示隐藏(关键的一步)
  analysisChart.on('legendselectchanged', (params) => {
    const selected = params.selected; 
    // 根据legend选中状态,更新yAxis的show属性
    const yAxisKeys = legendArr.value;
    const newOption = {
      yAxis: chartOption.value.yAxis.map((axis, index) => ({
        ...axis,
        show: selected[yAxisKeys[index]] ?? true,
      })),
      legend: {
        ...chartOption.value.legend,
        selected: selected
      }
    };
    // 合并新配置,触发图表更新
    analysisChart.setOption(newOption);
  });
};
 
defineExpose({ initChart });
</script>
 
<style lang="scss" scoped>
.PNMFlowChart {
  width: 100%;
  height: 630px;
  margin-top: 20px;
 
  .chart {
    width: 100%;
    height: 100%;
  }
}
</style>

最终版效果图如下:

完成最终版需求的过程中,小编也踩过一些坑,更没有一蹴而就直达最终版,而是一步步拆解完成的,希望能给到需要做似需求的小伙伴的一些参考,有更好的方案也欢迎一起探讨

相关推荐
托马斯-酷涛15 小时前
基于Echarts的气象数据可视化网站系统的设计与实现(Python版)
python·信息可视化·echarts
鱼钓猫1 天前
echarts 堆叠柱状图 柱与柱之间设置上下水平间隙
前端·echarts
小彭努力中3 天前
153.在 Vue 3 中使用 OpenLayers + Cesium 实现 2D/3D 地图切换效果
前端·javascript·vue.js·3d·ecmascript·echarts
马腾化云东3 天前
本地部署mcp-server-chart服务:从零到生产的完整指南,实现AI智能图表可视化
echarts·ai编程·mcp
魔都吴所谓4 天前
【Echarts】 电影票房汇总实时数据横向柱状图比图
javascript·ecmascript·echarts
JosieBook4 天前
【web应用】若依框架中,使用Echarts导出报表为PDF文件
前端·pdf·echarts
小彭努力中6 天前
147.在 Vue3 中使用 OpenLayers 地图上 ECharts 模拟飞机循环飞行
前端·javascript·vue.js·ecmascript·echarts
姜太小白7 天前
【ECharts】多个ECharts版本共存解决方案
前端·javascript·echarts
患得患失9497 天前
【前端】【Echarts】ECharts 词云图(WordCloud)教学详解
前端·javascript·echarts