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

如何为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(); 
  } 
); 

关键修改点说明

  1. 数据源处理:我模拟了第二组数据usdgbpData,你可以替换成自己的真实JSON请求(比如调用Highcharts.getJSON请求另一接口)。
  2. 主图系列:在主图的series数组里新增了第二个Area系列,对应第二组数据。
  3. 细节图初始化:createDetail函数中循环处理主图的两个系列,生成各自的初始细节数据,并在细节图的series里添加第二个系列。
  4. 选择事件更新:在主图的selection事件里,同时收集两个系列的选中区间数据,然后调用setData更新细节图的两个系列。
  5. Tooltip优化:修改了tooltip的formatter,支持显示多个系列的名称和数据,同时开启了图例方便区分系列。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 15:52:41