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

如何重写levelEvents函数解决自定义时间间隔条形/丝带图重叠问题

时间间隔条形图示例

问题核心错误

  1. 重叠判断逻辑完全反向:原有doesOverlap函数的判断条件实际是校验两个区间不重叠,导致分层筛选逻辑从根上出错。
  2. 分层规则不符合要求:原有递归逻辑没有优先将开始时间更晚的事件分配到上层,也无法保证每层内部事件无重叠。
  3. 重叠计数逻辑存在索引错误:getOverlaps函数中错误使用sortedEvents[i + j]取值,计算出的最大重叠数完全错误。

修正方案

1. 基础工具函数

// 正确的两个区间重叠判断
const isOverlap = (eventA, eventB) => {
  return eventA.dayStart < eventB.dayEnd && eventB.dayStart < eventA.dayEnd;
};

2. 重写分层函数

不需要递归,单次遍历即可完成分层,同时满足三个要求:使用最少行数、上层放置开始时间更晚的事件、每层内部事件无重叠:

// 返回值为二维数组,索引越小代表层级越靠上,每个子数组为当前层要渲染的事件
const levelEvents = (events) => {
  // 先按开始时间降序排序,保证晚开始的事件优先分配上层
  const sortedEvents = [...events].sort((a, b) => b.dayStart - a.dayStart);
  const levels = [];

  sortedEvents.forEach(currentEvent => {
    // 从最上层开始找可放入的层级
    for (let i = 0; i < levels.length; i++) {
      const lastEvent = levels[i].at(-1);
      // 当前层最后一个事件的结束时间早等于当前事件的开始时间,无重叠可放入
      if (lastEvent.dayEnd <= currentEvent.dayStart) {
        levels[i].push(currentEvent);
        return;
      }
    }
    // 现有层级都放不下,新增层级
    levels.push([currentEvent]);
  });

  // 每层内部按开始时间升序排序,方便从左到右渲染
  return levels.map(level => level.sort((a, b) => a.dayStart - b.dayStart));
};

3. 可选:修正最大重叠数计算

如果不需要单独计算重叠数,可以直接取levelEvents(events).length作为需要渲染的总行数,比单独计算效率更高。如果需要单独的计数函数,使用扫描线算法实现:

const getMaxOverlap = (events) => {
  if (!events?.length) return 0;
  const timePoints = [];
  events.forEach(event => {
    timePoints.push({ time: event.dayStart, type: 1 });
    timePoints.push({ time: event.dayEnd, type: -1 });
  });
  // 相同时间点先处理结束事件,避免相邻无重叠区间被误判为重叠
  timePoints.sort((a, b) => a.time - b.time || a.type - b.type);
  
  let maxOverlap = 0;
  let currentOverlap = 0;
  timePoints.forEach(point => {
    currentOverlap += point.type;
    maxOverlap = Math.max(maxOverlap, currentOverlap);
  });
  return maxOverlap;
};

4. 渲染逻辑调整

直接用分层结果渲染,不需要额外生成空数组:

{data.map((ribbon, order) => {
  const events = ribbonEvents[ribbon.id];
  const eventLevels = levelEvents(events);
  return eventLevels.map((levelEvents, level) => (
    <RibbonChart
      key={`${ribbon.id}-level-${level}`}
      ribbon={ribbon}
      ribbonEvents={levelEvents}
      level={level}
    />
  ));
})}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 15:51:02