146 lines
3.8 KiB
TypeScript
146 lines
3.8 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
|
|
}
|
|
|
|
enum WsStatus {
|
|
CONNECTING = 'connecting',
|
|
CONNECTED = 'connected',
|
|
DISCONNECTED = 'disconnected'
|
|
}
|
|
|
|
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, setTimelineTime, timelineDatetime } = useMap()
|
|
const [wsStatus, setWsStatus] = useState<WsStatus>(WsStatus.DISCONNECTED)
|
|
|
|
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 {
|
|
timelineDatetime,
|
|
setTimelineTime,
|
|
currentDatetime,
|
|
isPlaying,
|
|
speed,
|
|
startDate,
|
|
endDate,
|
|
play,
|
|
pause,
|
|
togglePlay,
|
|
skipForward,
|
|
skipBackward,
|
|
changeSpeed,
|
|
jumpToDate,
|
|
updateDate,
|
|
setTime,
|
|
wsStatus,
|
|
setWsStatus
|
|
}
|
|
}
|