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

Node.js项目中如何调整数据适配HighCharts堆叠柱形图需求

哈哈,我之前刚好做过类似的HighCharts堆叠柱场景,你已经搞定了取数和基础渲染,现在就差把数据调整成适配堆叠需求的结构对吧?给你一步步拆解:

步骤1:先把数据库原始数据理清楚

首先假设你从数据库拿到的是包含日期、小时、下雨状态的数组,第一步要做的是按日期分组,并且补全每个日期的0-23小时(避免有些小时没数据导致图表缺失):

// 示例原始数据(你从数据库查询回来的结构大概是这样)
const dbData = [
  { date: '2024-05-01', hour: 7, isRainy: true },
  { date: '2024-05-01', hour: 8, isRainy: true },
  { date: '2024-05-01', hour: 9, isRainy: false },
  { date: '2024-05-01', hour: 15, isRainy: true },
  { date: '2024-05-02', hour: 10, isRainy: true },
  { date: '2024-05-02', hour: 11, isRainy: true },
  { date: '2024-05-02', hour: 12, isRainy: true },
];

// 按日期分组,同时补全24小时的默认状态
const groupedData = {};
dbData.forEach(item => {
  if (!groupedData[item.date]) {
    // 初始化该日期的24小时,默认非下雨
    groupedData[item.date] = Array.from({ length: 24 }, (_, i) => ({
      hour: i,
      isRainy: false
    }));
  }
  // 更新对应小时的下雨状态
  groupedData[item.date][item.hour].isRainy = item.isRainy;
});
步骤2:转换成HighCharts需要的堆叠数据结构

这里分两种情况,看你的具体需求:

情况1:只区分“下雨/非下雨”两类堆叠

这种是最常见的,每个日期的柱形由“下雨小时数”和“非下雨小时数”堆叠而成:

// 提取所有日期作为X轴的类别
const xAxisCategories = Object.keys(groupedData);

// 构建下雨时段的系列数据:每个日期对应下雨的总小时数
const rainySeriesData = xAxisCategories.map(date => {
  return groupedData[date].filter(hourItem => hourItem.isRainy).length;
});

// 构建非下雨时段的系列数据:24减去下雨小时数
const nonRainySeriesData = xAxisCategories.map(date => {
  return 24 - rainySeriesData[xAxisCategories.indexOf(date)];
});

// 最终的系列数组
const series = [
  {
    name: '非下雨时段',
    data: nonRainySeriesData,
    color: '#e0e0e0', // 灰色
    stack: 'hours' // 关键:同一个stack名称才会堆叠
  },
  {
    name: '下雨时段',
    data: rainySeriesData,
    color: '#1f77b4', // 蓝色
    stack: 'hours'
  }
];

情况2:按连续下雨时段拆分堆叠(比如一天有两个下雨时段就显示两个蓝色块)

如果需要更精细地展示每个连续的下雨时段,就得先合并连续的小时段,再为每个时段创建系列:

// 辅助函数:把连续的下雨小时合并成时段
function getContinuousRainyPeriods(hourList) {
  const periods = [];
  let currentStart = null;
  
  hourList.forEach((item, index) => {
    if (item.isRainy) {
      if (currentStart === null) currentStart = item.hour;
      // 检查下一个小时是否非下雨,或者是最后一个小时
      if (index === hourList.length - 1 || !hourList[index + 1].isRainy) {
        periods.push({
          start: currentStart,
          end: item.hour,
          duration: item.hour - currentStart + 1
        });
        currentStart = null;
      }
    }
  });
  return periods;
}

// 提取日期列表
const xAxisCategories = Object.keys(groupedData);

// 初始化系列:先加一个非下雨的基础层
const series = [{
  name: '非下雨时段',
  data: xAxisCategories.map(() => 24),
  color: '#e0e0e0',
  stack: 'hours'
}];

// 为每个连续下雨时段创建系列
xAxisCategories.forEach(date => {
  const periods = getContinuousRainyPeriods(groupedData[date]);
  periods.forEach((period) => {
    // 避免重复创建相同时段的系列
    let existingSeries = series.find(s => s.name === `下雨时段 ${period.start}-${period.end}`);
    if (!existingSeries) {
      existingSeries = {
        name: `下雨时段 ${period.start}-${period.end}`,
        data: new Array(xAxisCategories.length).fill(0),
        color: '#1f77b4',
        stack: 'hours'
      };
      series.push(existingSeries);
    }
    // 填充当前日期的该时段时长
    existingSeries.data[xAxisCategories.indexOf(date)] = period.duration;
  });
});
步骤3:在Handlebars模板中渲染图表

在Node.js端把整理好的xAxisCategories和series转换成JSON,然后在Handlebars模板里无转义输出(用{{{}}}避免转义问题):

<!-- 你的Handlebars模板 -->
<div id="rain-chart" style="width: 100%; height: 400px;"></div>

<script>
  // 从模板接收后端传递的数据
  const chartConfig = {
    xAxisCategories: {{{JSON.stringify(xAxisCategories)}}},
    series: {{{JSON.stringify(series)}}}
  };

  // 初始化HighCharts
  Highcharts.chart('rain-chart', {
    chart: { type: 'column' },
    title: { text: '每日下雨时段堆叠柱形图' },
    xAxis: {
      categories: chartConfig.xAxisCategories,
      title: { text: '日期' }
    },
    yAxis: {
      min: 0,
      max: 24,
      title: { text: '小时数' },
      stackLabels: { enabled: true, fontWeight: 'bold' }
    },
    tooltip: {
      headerFormat: '<b>{point.x}</b><br/>',
      pointFormat: '{series.name}: {point.y} 小时'
    },
    plotOptions: {
      column: {
        stacking: 'normal',
        dataLabels: { enabled: true, color: 'white' }
      }
    },
    series: chartConfig.series
  });
</script>

注意: 如果你用的是Express+Handlebars,记得在路由里把xAxisCategories和series作为模板变量传递过去。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:18:30