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>

最终版效果图如下:

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

相关推荐
码界筑梦坊5 小时前
105-基于Flask的珍爱网相亲数据可视化分析系统
python·ai·信息可视化·flask·毕业设计·echarts
安卓开发者6 小时前
深入理解Android Kotlin Flow:响应式编程的现代实践
android·kotlin·echarts
别来无恙1493 天前
Spring Boot + ECharts 极简整合指南:从零实现动态数据可视化大屏
spring boot·信息可视化·echarts
莲青见卿3 天前
react+echarts实现变化趋势缩略图
javascript·react.js·echarts
爱吃香菇的小白菜4 天前
echarts、antv图表类 y轴范围 计算方法
前端·echarts
uppp»6 天前
echarts在前后端分离项目中的实践与应用
前端·javascript·echarts
小白的代码日记6 天前
使用 ECharts 实现小区住户数量统计柱状图
前端·javascript·echarts
CF14年老兵6 天前
Vue3 + ECharts 实现动态地图切换与平滑过渡动画
vue.js·echarts·trae
Pocker_Spades_A6 天前
AI 对话高效输入指令攻略(四):AI+Apache ECharts:生成各种专业图表
人工智能·echarts
GIS学姐嘉欣7 天前
无偿分享120套开源数据可视化大屏H5模板
信息可视化·echarts