ReactJS实现点击柱状图柱子后原图保留并右侧显示新图表
解决方案
Highcharts默认的drilldown功能会直接替换当前图表,要实现原图表缩小、右侧展示新图表的效果,需要放弃默认的替换逻辑,改为独立渲染两个图表并通过React状态控制布局和数据,具体实现如下:
核心思路
- 用React状态管理子图表的显示状态和数据
- 主图表通过自定义点击事件触发子图表数据加载,不依赖Highcharts的drilldown模块
- 借助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
相关产品推荐
相关产品推荐

