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

ReactJS实现点击柱状图柱子后原图保留并右侧显示新图表

解决方案

Highcharts默认的drilldown功能会直接替换当前图表,要实现原图表缩小、右侧展示新图表的效果,需要放弃默认的替换逻辑,改为独立渲染两个图表并通过React状态控制布局和数据,具体实现如下:

核心思路

  1. 用React状态管理子图表的显示状态和数据
  2. 主图表通过自定义点击事件触发子图表数据加载,不依赖Highcharts的drilldown模块
  3. 借助CSS Flex布局,根据子图表的显示状态动态调整主图表宽度

修改后的完整代码

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

// 提取钻取数据为单独变量,便于维护
const drilldownSeriesMap = {
  Ingredients: [
    {
      name: "Negative",
      color: "#ff0000",
      data: [["Nutrients", 22], ["Sweetners", 13], ["Organic/Natural", 2]]
    },
    {
      name: "Mixed",
      color: "#C8C6C6",
      data: [["Nutrients", 4], ["Sweetners", 2], ["Organic/Natural", 2]]
    },
    {
      name: "Neutral",
      color: "yellow",
      data: [["Nutrients", 0], ["Sweetners", 0], ["Organic/Natural", 0]]
    },
    {
      name: "Positive",
      color: "#00b050",
      data: [["Nutrients", 25], ["Sweetners", 12], ["Organic/Natural", 18]]
    }
  ],
  Taste: [
    {
      name: "Negative",
      color: "#ff0000",
      data: [["Baby's Choice", 37], ["Parent's Choice", 5]]
    },
    {
      name: "Mixed",
      color: "#C8C6C6",
      data: [["Baby's Choice", 9], ["Parent's Choice", 4]]
    },
    {
      name: "Neutral",
      color: "yellow",
      data: [["Baby's Choice", 0], ["Parent's Choice", 0]]
    },
    {
      name: "Positive",
      color: "#00b050",
      data: [["Baby's Choice", 42], ["Parent's Choice", 8]]
    }
  ]
};

export const DrillDown = () => {
  const [showDrilldownChart, setShowDrilldownChart] = useState(false);
  const [drilldownChartData, setDrilldownChartData] = useState([]);
  const [drilldownTitle, setDrilldownTitle] = useState("");

  // 主图表配置
  const mainChartOptions = {
    chart: {
      type: "bar",
      height: 650
    },
    title: {
      text: "Category Drivers",
      align: "left",
      style: {
        fontWeight: "200",
        fontSize: "2vw"
      }
    },
    xAxis: {
      type: "category"
    },
    yAxis: {
      min: 0,
      labels: {
        formatter: function() {
          return this.axis.defaultLabelFormatter.call(this) + "%";
        }
      },
      tickInterval: 10,
      stackLabels: {
        enabled: true,
        style: {
          fontWeight: 'bold',
          color: "gray",
          textOutline: 'none'
        }
      }
    },
    legend: {
      enabled: true,
      reversed: true
    },
    tooltip: {
      enabled: false
    },
    plotOptions: {
      bar: {
        stacking: "normal"
      },
      series: {
        borderWidth: 0,
        dataLabels: {
          enabled: false
        },
        states: {
          hover: {
            enabled: false
          }
        },
        point: {
          // 自定义点击事件,触发钻取逻辑
          events: {
            click: function(e) {
              const pointName = this.name;
              // 仅处理可钻取的柱子
              if (drilldownSeriesMap[pointName]) {
                setDrilldownChartData(drilldownSeriesMap[pointName]);
                setDrilldownTitle(pointName);
                setShowDrilldownChart(true);
              }
            }
          }
        }
      }
    },
    series: [
      {
        name: "Negative",
        color: "#ff0000",
        data: [
          { name: "Baby Tolerance", y: 12 },
          { name: "Ingredients", y: 16 },
          { name: "Health Benefits", y: 6 },
          { name: "Taste", y: 9 },
          { name: "Peer Recommendation", y: 0.5 },
          { name: "Expert Advocacy", y: 2 },
          { name: "Brand Value", y: 3.5 },
          { name: "Convenience", y: 2 },
          { name: "Price", y: 3.5 }
        ]
      },
      {
        name: "Mixed",
        color: "#bfbfbf",
        data: [
          { name: "Baby Tolerance", y: 8 },
          { name: "Ingredients", y: 4 },
          { name: "Health Benefits", y: 3 },
          { name: "Taste", y: 3 },
          { name: "Peer Recommendation", y: 11 },
          { name: "Expert Advocacy", y: 3 },
          { name: "Brand Value", y: 0.5 },
          { name: "Convenience", y: 3 },
          { name: "Price", y: 0.5 }
        ]
      },
      {
        name: "Neutral",
        color: "#ffc000",
        data: [
          { name: "Baby Tolerance", y: 1 },
          { name: "Ingredients", y: 0 },
          { name: "Health Benefits", y: 0 },
          { name: "Taste", y: 0 },
          { name: "Peer Recommendation", y: 0 },
          { name: "Expert Advocacy", y: 0 },
          { name: "Brand Value", y: 0 },
          { name: "Convenience", y: 0 },
          { name: "Price", y: 0 }
        ]
      },
      {
        name: "Positive",
        color: "#00b050",
        data: [
          { name: "Baby Tolerance", y: 20 },
          { name: "Ingredients", y: 15 },
          { name: "Health Benefits", y: 11 },
          { name: "Taste", y: 7 },
          { name: "Peer Recommendation", y: 5 },
          { name: "Expert Advocacy", y: 11 },
          { name: "Brand Value", y: 11 },
          { name: "Convenience", y: 6 },
          { name: "Price", y: 3 }
        ]
      }
    ]
  };

  // 子图表配置
  const drilldownChartOptions = {
    chart: {
      type: "bar",
      height: 650
    },
    title: {
      text: drilldownTitle,
      align: "left",
      style: {
        fontWeight: "200",
        fontSize: "2vw"
      }
    },
    xAxis: {
      type: "category"
    },
    yAxis: {
      min: 0,
      labels: {
        formatter: function() {
          return this.axis.defaultLabelFormatter.call(this) + "%";
        }
      },
      tickInterval: 10
    },
    legend: {
      enabled: true,
      reversed: true
    },
    tooltip: {
      enabled: false
    },
    plotOptions: {
      bar: {
        stacking: "normal"
      },
      series: {
        borderWidth: 0,
        dataLabels: {
          enabled: false
        },
        states: {
          hover: {
            enabled: false
          }
        }
      }
    },
    series: drilldownChartData
  };

  return (
    <div style={{ display: "flex", width: "100%", gap: "20px" }}>
      <div style={{ width: showDrilldownChart ? "50%" : "100%" }}>
        <HighchartsReact highcharts={Highcharts} options={mainChartOptions} />
      </div>
      {showDrilldownChart && (
        <div style={{ width: "50%" }}>
          <HighchartsReact highcharts={Highcharts} options={drilldownChartOptions} />
        </div>
      )}
    </div>
  );
};

关键修改点

  • 移除默认drilldown模块:不再使用Highcharts自带的钻取替换逻辑,改用自定义点击事件
  • 状态管理:新增showDrilldownChart控制子图表显示,drilldownChartData存储子图表数据
  • 布局控制:用Flex容器包裹两个图表,根据子图表显示状态动态调整主图表宽度
  • 点击事件重构:在主图表的plotOptions.series.point.events.click中处理柱子点击,加载对应子图表数据
  • 数据解耦:将钻取数据提取为独立对象,便于后续维护和扩展

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 10:03:21