如何为Highcharts主从(Master-Detail)图表添加第二个Area类型系列
在Highcharts主从图表中添加第二个Area系列的解决方案
我来帮你搞定在Highcharts主从图表里添加第二个Area系列的问题,核心是要同步主图和细节图的两个系列数据,同时处理主图选择事件里的双系列更新逻辑。下面分步骤说明并给出修改后的完整代码:
关键步骤拆解
- 准备第二个系列的数据:你需要有第二个和主数据结构一致的数据源(比如和示例中汇率数据格式相同的另一组数据),这里我们可以模拟数据或者引入真实数据源。
- 主图添加第二个系列:在主图的
series数组里新增一个Area类型的系列,对应第二组数据。 - 细节图初始化双系列:在
createDetail函数中,同时生成两个系列的初始细节数据,而不是只处理第一个系列。 - 同步选择事件的双系列更新:在主图的
selection事件回调里,要同时收集两个系列的选中区间数据,并更新细节图的两个系列。
修改后的完整代码
const priceChart1 = Highcharts.getJSON( 'https://cdn.jsdelivr.net/gh/highcharts/highcharts@v7.0.0/samples/data/usdeur.json', (usdeurData) => { // 模拟第二个系列的数据(你可以替换成自己的真实数据源请求) const usdgbpData = usdeurData.map(item => [item[0], item[1] + (Math.random() - 0.5) * 0.1]); let detailChart; // 创建细节图表函数,修改为处理双系列 function createDetail(masterChart) { let detailData1 = [], detailData2 = [], detailStart = usdeurData[0][0]; // 同时处理主图的两个系列数据 masterChart.series[0].data.forEach(point => { if (point.x >= detailStart) { detailData1.push(point.y); } }); masterChart.series[1].data.forEach(point => { if (point.x >= detailStart) { detailData2.push(point.y); } }); detailChart = Highcharts.chart('detail-container', { chart: { zoomType: "x", spacingLeft: 10, spacingRight: -20, borderRadius: 10, backgroundColor: "#F3F3F3", borderColor: "#335cad", height: priceChartHeight, style: { fontFamily: "Manrope", position: 'absolute' }, // 合并重复的style配置 resetZoomButton: { position: { x: -40, y: 5 }, theme: { fill: '#377DED', stroke: 'transparent', r: 0, style: { color: 'white', borderRadius: 10 }, states: { hover: { fill: '#41739D', style: { color: 'white' } } } } }, marginBottom: 90, reflow: false, marginLeft: 10 }, credits: { enabled: false }, title: { text: null, align: 'left' }, subtitle: { text: null, align: 'left' }, xAxis: { type: 'datetime', visible: false }, yAxis: { title: { text: null }, opposite: true, gridLineColor: "rgba(87, 87, 87, 0.15)", gridLineDashStyle: "dash", left: -40 }, tooltip: { formatter: function () { // 修改tooltip以显示两个系列的数据 let tooltipHtml = '<br/>'; this.points.forEach(point => { tooltipHtml += ` <span style="font-weight: 700;font-size: 14px; line-height: 19px; color: #377DED;"> ${Highcharts.numberFormat(point.y, 2)} </span> <span style="font-size: 9px; font-weight: 300; line-height: 12px; color: rgba(51,51,51, 0.25)">${point.series.name}</span><br/> `; }); tooltipHtml += `<span style="font-size: 9px; font-weight: 300; line-height: 12px; color: rgba(51,51,51, 0.25)">${Highcharts.dateFormat('%e %B %Y', this.x)}</span>`; return tooltipHtml; }, shared: true, borderRadius: 5, borderColor: 'transparent', shadow: false, backgroundColor: '#fff' }, legend: { enabled: true }, // 开启图例区分两个系列 plotOptions: { series: { marker: { enabled: false, states: { hover: { enabled: true, radius: 3 } } } } }, // 新增第二个Area系列 series: [ { name: 'USD to EUR', data: detailData1, type: 'area' }, { name: 'USD to GBP', data: detailData2, type: 'area' } ], exporting: { enabled: false } }); } // 创建主图表函数,添加第二个系列 function createMaster() { Highcharts.chart('master-container', { chart: { reflow: false, borderWidth: 0, backgroundColor: null, spacingLeft: 10, spacingRight: 30, borderRadius: 10, zoomType: 'x', events: { selection: function (event) { var extremesObject = event.xAxis[0], min = extremesObject.min, max = extremesObject.max, detailData1 = [], detailData2 = [], xAxis = this.xAxis[0]; // 同时收集两个系列的选中区间数据 this.series[0].data.forEach(point => { if (point.x > min && point.x < max) { detailData1.push([point.x, point.y]); } }); this.series[1].data.forEach(point => { if (point.x > min && point.x < max) { detailData2.push([point.x, point.y]); } }); // 更新遮罩带逻辑不变 xAxis.removePlotBand('mask-before'); xAxis.addPlotBand({ id: 'mask-before', from: usdeurData[0][0], to: min, color: 'rgba(0, 0, 0, 0)' }); xAxis.removePlotBand('mask-after'); xAxis.addPlotBand({ id: 'mask-after', from: max, to: usdeurData[usdeurData.length - 1][0], color: 'rgba(0, 0, 0, 0)' }); xAxis.addPlotBand({ id: 'mask-selected', from: min, to: max, color: 'rgba(255, 255, 255, 1)', borderColor: "#377DED", borderWidth: 2 }); // 同时更新细节图的两个系列 detailChart.series[0].setData(detailData1); detailChart.series[1].setData(detailData2); return false; } } }, title: { text: null }, accessibility: { enabled: false }, xAxis: { type: "datetime", labels: { format: '{value:%b %e }' }, crosshair: { color: '#377DED80' }, lineWidth: 0, minorGridLineWidth: 0, lineColor: 'transparent', minorTickLength: 0, tickLength: 0, top: -5, showLastTickLabel: true, maxZoom: 14 * 24 * 3600000, plotBands: [{ id: 'mask-before', from: usdeurData[0][0], to: usdeurData[usdeurData.length - 1][0], color: 'rgba(0, 0, 0, 0)' }], title: { text: null } }, yAxis: { gridLineWidth: 0, labels: { enabled: false }, title: { text: null }, min: 0.6, showFirstLabel: false }, tooltip: { borderRadius: 50, borderColor: 'red' }, legend: { enabled: true }, // 开启主图图例 credits: { enabled: false }, plotOptions: { series: { fillColor: { linearGradient: [0, 0, 0, 70], stops: [ [0, Highcharts.getOptions().colors[0]], [1, 'rgba(255,255,255,0)'] ] }, lineWidth: 1, marker: { enabled: false }, shadow: false, states: { hover: { lineWidth: 1 } }, enableMouseTracking: false } }, // 新增第二个主图系列 series: [ { type: 'area', name: 'USD to EUR', pointInterval: 24 * 3600 * 1000, pointStart: usdeurData[0][0], data: usdeurData }, { type: 'area', name: 'USD to GBP', pointInterval: 24 * 3600 * 1000, pointStart: usdgbpData[0][0], data: usdgbpData } ], exporting: { enabled: false } }, masterChart => { createDetail(masterChart); }); } const container = document.getElementById('price-chart-main'); container.style.position = 'relative'; container.innerHTML += '<div id="detail-container" style="height: 100%"></div><div id="master-container" style="height: 90px; position: absolute; bottom: 0; width: 100%"></div>'; createMaster(); } );
关键修改点说明
- 数据源处理:我模拟了第二组数据
usdgbpData,你可以替换成自己的真实JSON请求(比如调用Highcharts.getJSON请求另一接口)。 - 主图系列:在主图的
series数组里新增了第二个Area系列,对应第二组数据。 - 细节图初始化:
createDetail函数中循环处理主图的两个系列,生成各自的初始细节数据,并在细节图的series里添加第二个系列。 - 选择事件更新:在主图的
selection事件里,同时收集两个系列的选中区间数据,然后调用setData更新细节图的两个系列。 - Tooltip优化:修改了tooltip的formatter,支持显示多个系列的名称和数据,同时开启了图例方便区分系列。
内容的提问来源于stack exchange,提问作者Yatupic
相关产品推荐
相关产品推荐

