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

React实现随当前时间滚动的实时时间线(仿Google Calendar)

实现实时同步的日历时间线(类似Google Calendar)

核心思路

放弃CSS动画方案,直接通过计算当前时间在工作时间范围内的比例定位时间线,结合useEffect定时更新位置,确保页面刷新或多设备打开时位置完全一致。

步骤分解

  1. 定义时间范围常量:将早8到晚5的9小时(540分钟)转换成当天的分钟数,方便后续计算偏移量。
  2. 计算当前时间偏移比例:把当前时间转为当天总分钟数,算出它相对于早8点的偏移占总工作时长的比例。
  3. 映射到容器位置:用偏移比例乘以日历容器的内容总高度,得到时间线的垂直位置。
  4. 定时更新:每分钟重新计算一次位置(时间每分钟变化一次,足够满足精度需求)。

完整代码示例(结合Chakra UI)

import { useState, useEffect, useRef } from 'react';
import { Box } from '@chakra-ui/react';

const CalendarTimeline = () => {
  const [timelineTop, setTimelineTop] = useState(0);
  // 引用可滚动的日历容器,用于获取内容实际高度
  const calendarContainerRef = useRef(null);

  // 计算时间线位置的核心函数
  const updateTimelinePosition = () => {
    const container = calendarContainerRef.current;
    if (!container) return;

    // 工作时间范围:8:00 - 17:00,转为当天分钟数
    const startMinutes = 8 * 60;
    const endMinutes = 17 * 60;
    const totalWorkingMinutes = endMinutes - startMinutes;

    // 当前时间转为当天总分钟数
    const now = new Date();
    const currentMinutes = now.getHours() * 60 + now.getMinutes();

    // 计算位置比例,处理早于8点或晚于5点的边界情况
    let positionRatio = 0;
    if (currentMinutes < startMinutes) {
      positionRatio = 0;
    } else if (currentMinutes > endMinutes) {
      positionRatio = 1;
    } else {
      positionRatio = (currentMinutes - startMinutes) / totalWorkingMinutes;
    }

    // 获取容器内容的实际总高度(包含滚动区域)
    const contentTotalHeight = container.scrollHeight;
    // 计算时间线的top值
    setTimelineTop(positionRatio * contentTotalHeight);
  };

  useEffect(() => {
    // 页面加载时立即计算一次位置
    updateTimelinePosition();
    // 每分钟更新一次,避免不必要的性能消耗
    const intervalId = setInterval(updateTimelinePosition, 60 * 1000);

    // 组件卸载时清除定时器
    return () => clearInterval(intervalId);
  }, []);

  return (
    <Box
      ref={calendarContainerRef}
      overflowY="auto"
      position="relative"
      height="80vh"
      border="1px solid #e2e8f0"
    >
      {/* 日历内容区域:替换为你的时间刻度、事件等内容 */}
      {/* 示例用固定高度模拟9小时时间轴,实际根据需求调整 */}
      <Box height="900px">
        {/* 日历内容 */}
      </Box>

      {/* 当前时间线 */}
      <Box
        position="absolute"
        left={0}
        right={0}
        height="2px"
        bg="red.500"
        top={timelineTop}
        zIndex={10}
        opacity={0.8}
      />
    </Box>
  );
};

export default CalendarTimeline;

关键细节说明

  • 容器定位:日历容器必须设置position: relative,确保时间线的absolute定位相对于容器生效。
  • 内容高度:用scrollHeight获取容器内容的实际总高度,不管容器是否滚动,都能准确映射时间比例;如果日历高度是固定值(比如每小时100px),直接用固定值代替scrollHeight性能更优。
  • 边界处理:当当前时间在工作时间外时,时间线会固定在容器顶部或底部,避免出现异常位置。
  • 性能优化:每分钟更新一次足够满足视觉需求,无需每秒更新,减少不必要的重渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 21:35:00