React实现随当前时间滚动的实时时间线(仿Google Calendar)
实现实时同步的日历时间线(类似Google Calendar)
核心思路
放弃CSS动画方案,直接通过计算当前时间在工作时间范围内的比例定位时间线,结合useEffect定时更新位置,确保页面刷新或多设备打开时位置完全一致。
步骤分解
- 定义时间范围常量:将早8到晚5的9小时(540分钟)转换成当天的分钟数,方便后续计算偏移量。
- 计算当前时间偏移比例:把当前时间转为当天总分钟数,算出它相对于早8点的偏移占总工作时长的比例。
- 映射到容器位置:用偏移比例乘以日历容器的内容总高度,得到时间线的垂直位置。
- 定时更新:每分钟重新计算一次位置(时间每分钟变化一次,足够满足精度需求)。
完整代码示例(结合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
相关产品推荐
相关产品推荐

