mosaicmap/hooks/use-timeline.ts
2025-07-21 21:27:35 +08:00

125 lines
3.2 KiB
TypeScript

import { useState, useEffect, useRef, useCallback } from 'react'
import { addDays, subDays } from 'date-fns'
import { useMap } from '@/app/map-context'
interface UseTimelineOptions {
startDate?: Date
endDate?: Date
initialDate?: Date
onDateChange?: (date: Date) => void
autoPlay?: boolean
}
export function useTimeline({
startDate = subDays(new Date(), 30),
endDate = new Date(),
initialDate = new Date(),
onDateChange,
autoPlay = false
}: UseTimelineOptions = {}) {
const [currentDate, setCurrentDate] = useState(initialDate)
const [isPlaying, setIsPlaying] = useState(autoPlay)
const [speed, setSpeed] = useState<'slow' | 'normal' | 'fast'>('normal')
const intervalRef = useRef<NodeJS.Timeout | null>(null)
const { setTime } = useMap()
const speedIntervals = {
slow: 2000,
normal: 1000,
fast: 500
}
const updateDate = useCallback((newDate: Date) => {
setCurrentDate(newDate)
onDateChange?.(newDate)
}, [onDateChange])
const play = useCallback(() => {
setIsPlaying(true)
}, [])
const pause = useCallback(() => {
setIsPlaying(false)
}, [])
const togglePlay = useCallback(() => {
setIsPlaying(prev => !prev)
}, [])
const skipForward = useCallback(() => {
const newDate = addDays(currentDate, 1)
if (newDate <= endDate) {
updateDate(newDate)
}
}, [currentDate, endDate, updateDate])
const skipBackward = useCallback(() => {
const newDate = subDays(currentDate, 1)
if (newDate >= startDate) {
updateDate(newDate)
}
}, [currentDate, startDate, updateDate])
const changeSpeed = useCallback((newSpeed: 'slow' | 'normal' | 'fast') => {
setSpeed(newSpeed)
}, [])
const jumpToDate = useCallback((date: Date) => {
if (date >= startDate && date <= endDate) {
updateDate(date)
}
}, [startDate, endDate, updateDate])
// 自动播放逻辑
useEffect(() => {
if (isPlaying) {
intervalRef.current = setInterval(() => {
const nextDate = addDays(currentDate, 1)
if (nextDate <= endDate) {
updateDate(nextDate)
} else {
// 到达结束日期,停止播放
setIsPlaying(false)
}
}, speedIntervals[speed])
} else {
if (intervalRef.current) {
clearInterval(intervalRef.current)
intervalRef.current = null
}
}
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current)
}
}
}, [isPlaying, currentDate, endDate, speed, updateDate])
// 清理定时器
useEffect(() => {
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current)
}
}
}, [])
return {
currentDate,
isPlaying,
speed,
startDate,
endDate,
play,
pause,
togglePlay,
skipForward,
skipBackward,
changeSpeed,
jumpToDate,
updateDate,
setTime
}
}