You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ReactJS中三个HighCharts组件合并图例独立展示的实现方法

在React中实现HighCharts多图表的合并独立图例

核心思路

通过收集所有图表实例,提取并合并系列数据,利用HighCharts的API在独立容器中生成统一图例,同时实现图例与所有图表的交互同步。

具体实现步骤及代码示例

以下基于highcharts-react-official(React官方推荐的HighCharts封装库)实现:

import React, { useState, useEffect } from 'react';
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';

const CombinedLegendDemo = () => {
  // 存储三个图表的实例
  const [chartInstances, setChartInstances] = useState([]);
  // 存储合并图例的图表实例
  const [legendChart, setLegendChart] = useState(null);

  // 单个图表配置:隐藏自带图例
  const chart1Opts = {
    series: [{ name: '系列A', data: [1,3,5], color: '#ff4d4f' }, { name: '系列B', data: [2,4,6], color: '#52c41a' }],
    legend: { enabled: false }
  };

  const chart2Opts = {
    series: [{ name: '系列B', data: [7,9,11], color: '#52c41a' }, { name: '系列C', data: [8,10,12], color: '#1890ff' }],
    legend: { enabled: false }
  };

  const chart3Opts = {
    series: [{ name: '系列A', data: [13,15,17], color: '#ff4d4f' }, { name: '系列C', data: [14,16,18], color: '#1890ff' }],
    legend: { enabled: false }
  };

  // 获取每个图表的实例
  const saveChartInstance = (chart) => {
    setChartInstances(prev => {
      if (!prev.includes(chart)) return [...prev, chart];
      return prev;
    });
  };

  // 创建合并图例
  useEffect(() => {
    // 确保三个图表实例都已获取
    if (chartInstances.length !== 3 || legendChart) return;

    // 提取所有系列并按名称去重
    const seriesMap = {};
    chartInstances.forEach(chart => {
      chart.series.forEach(series => {
        if (!seriesMap[series.name]) {
          seriesMap[series.name] = {
            name: series.name,
            color: series.color,
            visible: series.visible
          };
        }
      });
    });
    const combinedSeries = Object.values(seriesMap);

    // 配置仅显示图例的空图表
    const legendConfig = {
      chart: {
        renderTo: 'combined-legend',
        backgroundColor: 'transparent',
        borderWidth: 0,
        height: 60 // 仅保留图例高度
      },
      series: combinedSeries.map(s => ({
        name: s.name,
        color: s.color,
        visible: s.visible,
        data: [] // 空数据,不绘制图表内容
      })),
      legend: { enabled: true, align: 'center', verticalAlign: 'middle' },
      xAxis: { enabled: false },
      yAxis: { enabled: false },
      tooltip: { enabled: false },
      plotOptions: {
        series: {
          events: {
            // 点击图例时同步所有图表的对应系列可见性
            legendItemClick: function() {
              const targetName = this.name;
              chartInstances.forEach(chart => {
                chart.series.forEach(s => {
                  if (s.name === targetName) s.setVisible(!s.visible, false);
                });
                chart.redraw();
              });
              return false; // 阻止默认单图表系列切换
            }
          }
        }
      }
    };

    // 生成图例图表
    const newLegendChart = new Highcharts.Chart(legendConfig);
    setLegendChart(newLegendChart);

    // 监听原图表系列的可见性变化,同步图例状态
    chartInstances.forEach(chart => {
      chart.series.forEach(series => {
        series.on('visibilityChange', function() {
          const legendSeries = newLegendChart.series.find(s => s.name === this.name);
          if (legendSeries && legendSeries.visible !== this.visible) {
            legendSeries.setVisible(this.visible, false);
            newLegendChart.redraw();
          }
        });
      });
    });

    // 组件卸载时销毁图例图表
    return () => {
      if (legendChart) legendChart.destroy();
    };
  }, [chartInstances, legendChart]);

  return (
    <div>
      {/* 三个图表组件 */}
      <div style={{ marginBottom: '20px' }}>
        <HighchartsReact highcharts={Highcharts} options={chart1Opts} callback={saveChartInstance} />
      </div>
      <div style={{ marginBottom: '20px' }}>
        <HighchartsReact highcharts={Highcharts} options={chart2Opts} callback={saveChartInstance} />
      </div>
      <div style={{ marginBottom: '20px' }}>
        <HighchartsReact highcharts={Highcharts} options={chart3Opts} callback={saveChartInstance} />
      </div>
      {/* 合并图例容器 */}
      <div id="combined-legend" style={{ border: '1px solid #f0f0f0', padding: '10px' }}></div>
    </div>
  );
};

export default CombinedLegendDemo;

关键细节说明

  1. 隐藏单图表图例:通过legend: { enabled: false }关闭每个图表自带的图例,避免重复显示。
  2. 实例收集与系列合并:通过callback获取每个图表的实例,提取所有系列并按名称去重,确保图例项不重复。
  3. 交互同步:
    • 点击合并图例时,遍历所有图表切换对应系列的可见性;
    • 监听原图表系列的visibilityChange事件,同步更新合并图例的状态。
  4. 资源清理:在组件卸载时销毁图例图表实例,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Klaudio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 09:36:19