mosaicmap/hooks/use-timeline.ts
2025-08-08 07:20:05 +08:00

135 lines
3.5 KiB
TypeScript

import { useState, useEffect, useRef, useCallback } from 'react'
import { addDays, subDays } from 'date-fns'
import { useMap } from '@/app/map-context'
import { useSubscription, gql } from '@apollo/client'
import { parse } from 'date-fns'
import { UTCDate } from "@date-fns/utc";
import { toZonedTime } from 'date-fns-tz';
const speedIntervals = {
slow: 2000,
normal: 1000,
fast: 500
}
interface UseTimelineOptions {
startDate?: Date
endDate?: Date
initialDate?: Date
onDateChange?: (date: Date) => void
autoPlay?: boolean
autoUpdate?: boolean
}
export function useTimeline({
startDate = subDays(new Date(), 30),
endDate = new Date(),
onDateChange,
autoPlay = false
}: UseTimelineOptions = {}) {
const [isPlaying, setIsPlaying] = useState(autoPlay)
const [speed, setSpeed] = useState<'slow' | 'normal' | 'fast'>('normal')
const intervalRef = useRef<NodeJS.Timeout | null>(null)
const { setTime, currentDatetime } = useMap()
const updateDate = useCallback((newDate: Date) => {
setTime(newDate)
onDateChange?.(newDate)
}, [onDateChange])
const play = useCallback(() => {
setIsPlaying(true)
}, [])
const pause = useCallback(() => {
setIsPlaying(false)
}, [])
const togglePlay = useCallback(() => {
setIsPlaying(prev => !prev)
}, [])
const skipForward = useCallback(() => {
if (!currentDatetime) return;
const newDate = addDays(currentDatetime, 1)
if (newDate <= endDate) {
updateDate(newDate)
}
}, [currentDatetime, endDate, updateDate])
const skipBackward = useCallback(() => {
if (!currentDatetime) return;
const newDate = subDays(currentDatetime, 1)
if (newDate >= startDate) {
updateDate(newDate)
}
}, [currentDatetime, 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(() => {
if (!currentDatetime) return;
const nextDate = addDays(currentDatetime, 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, currentDatetime, endDate, speed, updateDate])
// 清理定时器
useEffect(() => {
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current)
}
}
}, [])
return {
currentDatetime,
isPlaying,
speed,
startDate,
endDate,
play,
pause,
togglePlay,
skipForward,
skipBackward,
changeSpeed,
jumpToDate,
updateDate,
setTime
}
}