UNPKG

press-pix

Version:
146 lines (119 loc) 3.98 kB
import { transformTime, type TimeItem } from 't-comm/es/time/countdown'; import { useRef, useState, useEffect } from 'preact/hooks'; import type { CountDownProps, TimeData } from './type'; enum EnumCountDownStatus { active, inActive, paused, finished, } interface CountDownData { timeData: TimeData | null; timeList: TimeItem[]; timeText: string; status: EnumCountDownStatus; } type UseCountdownParams = Omit<CountDownProps, 'content' | 'size' | 'splitWithUnit' | 'theme'>; export const useCountDown = (params: UseCountdownParams) => { const { time, autoStart, millisecond, format = ' HH:mm:ss', onChange, onFinish, ascending } = params; const [{ timeText, timeData, timeList, status }, setCountDownData] = useState<CountDownData>({ timeData: null, timeList: [], timeText: '', status: EnumCountDownStatus.inActive, }); const ctxRef = useRef({ timerId: 0, remainTime: time }); // 处理毫秒级展示 const getProcessedFormat = (currentFormat: string) => { if (millisecond && !format.includes(':SSS')) return currentFormat.concat(':SSS'); return currentFormat; }; const clearCountDown = () => { const currentTimerId = ctxRef.current.timerId; if (currentTimerId) { window.cancelAnimationFrame(currentTimerId); ctxRef.current.timerId = 0; setCountDownData(state => ({ ...state, status: EnumCountDownStatus.inActive })); } }; let previousFrameTimestamp = 0; // pixui 这里的第一个参数可能为 null,真机 const tick = (timestamp = Date.now(), reset = false) => { let delta = timestamp - (previousFrameTimestamp || timestamp); if (ascending) { delta = -delta; } let nextRemainTime = reset ? time : ctxRef.current.remainTime - delta; ctxRef.current.remainTime = nextRemainTime; let nextStatus = EnumCountDownStatus.active; if (nextRemainTime <= 0) { nextRemainTime = 0; clearCountDown(); nextStatus = EnumCountDownStatus.finished; onFinish?.(); window.cancelAnimationFrame(ctxRef.current.timerId); } else { (ctxRef.current as any).timerId = window.requestAnimationFrame(tick); } const countDownData = { ...transformTime(nextRemainTime, getProcessedFormat(format)), status: nextStatus, }; onChange?.(countDownData.timeData); setCountDownData(countDownData); previousFrameTimestamp = timestamp; }; const startCountDown = (reset = false) => { clearCountDown(); (ctxRef.current as any).timerId = window.requestAnimationFrame(timestamp => tick(timestamp, reset)); }; useEffect(() => { if (autoStart) { startCountDown(true); } else { clearCountDown(); ctxRef.current.remainTime = time; const initialData = transformTime(time, getProcessedFormat(format)); setCountDownData({ ...initialData, status: EnumCountDownStatus.inActive, }); } return clearCountDown; // eslint-disable-next-line react-hooks/exhaustive-deps }, [time, millisecond, format, autoStart]); const start = () => { if (status === EnumCountDownStatus.active) return; startCountDown(); }; const pause = () => { clearCountDown(); setCountDownData(state => ({ ...state, status: EnumCountDownStatus.paused })); }; const reset = () => { clearCountDown(); if (autoStart) { startCountDown(true); } else { setCountDownData(state => ({ ...state, status: EnumCountDownStatus.inActive })); } }; const isActive = status === EnumCountDownStatus.active; const isInActive = status === EnumCountDownStatus.inActive; const isPaused = status === EnumCountDownStatus.paused; const isFinished = status === EnumCountDownStatus.finished; return { remainTime: ctxRef.current.remainTime, timeText, timeData, timeList, start, pause, reset, isActive, isInActive, isPaused, isFinished, }; }; export { transformTime };