先声明一个补0的函数
// 先声明一个补0的函数
function formate(time: number): string {return `${time < 10 ? "0" : ""}${time}`;
}
定义最终返回的数据结构
export interface TimeInfo {/** 天 */day: number;/** 小时 */hours: number;/** 补零后的小时 */hoursStr: string;/** 分钟 */minutes: number;/** 补零后的分 */minutesStr: string;/** 秒 */seconds: number;/** 补零后的秒 */secondsStr: string;/** 毫秒 */milliseconds?: number;/** 补零后的毫秒 */millisecondsStr?: string;/** 倒计时状态 pending-初始化状态 runing-进行中 end-已结束 */status: "pending" | "runing" | "end";
}
工具方法——清除倒计时数据信息:
function clearCountdownInfo(showMillisecond = false, status?: TimeInfo["status"]): TimeInfo {const timeInfo: TimeInfo = {day: 0,hours: 0,hoursStr: "00",minutes: 0,minutesStr: "00",seconds: 0,secondsStr: "00",status: status || "end",};if (showMillisecond) {timeInfo.milliseconds = 0;timeInfo.millisecondsStr = "0";}return timeInfo;
}
关键工具方法——计算倒计时返回的数据信息:
function computeCountdownInfo(remainTime: number,showMillisecond = false
): TimeInfo {// 剩余时间小于说明结束,直接清空if (remainTime < 0) {return clearCountdownInfo(showMillisecond);}// 这里用了一个比较笨的方法,一个个进行计算,后续可以优化试试看const day = Math.floor(remainTime / (24 * 60 * 60));const hours = Math.floor((remainTime / (60 * 60)) % 24);const hoursStr = formate(hours);const minutes = Math.floor((remainTime / 60) % 60);const minutesStr = formate(minutes);const seconds = Math.floor(remainTime % 60);const secondsStr = formate(seconds);// 组合成需要返回的时间信息const timeInfo: TimeInfo = {day,hours,hoursStr,minutes,minutesStr,seconds,secondsStr,status: "runing",};// 需要显示毫秒逻辑处理if (showMillisecond) {const milliseconds = Math.floor(remainTime * 1000);// 只取首位const millisecondsStr = String(milliseconds).slice(-3);timeInfo.milliseconds = milliseconds;timeInfo.millisecondsStr = millisecondsStr;}return timeInfo;
}
定义函数:
interface CountDownOptions {showMillisecond: booleandeadlineTime: any
}function computeRemainTime(deadlineTime: number) {// 当前时间const nowTime = Date.now();// 截止时间 - 当前时间 = 剩余时间const remainTime = (deadlineTime - nowTime) / 1000;return remainTime;
}
核心逻辑 —— useCountdown hook 组件:
export const useCountdown = (options: CountDownOptions) => {// 首次初始化数据,显示清除的数据const [timeInfo, setTimeInfo] = useState<TimeInfo>(clearCountdownInfo(options.showMillisecond, "pending"));useEffect(() => {let timer = 0;function countdown() {const remainTime = computeRemainTime(options.deadlineTime);// 剩余时间大于 0 才开始倒计时if (remainTime > 0) {// 未结束时直接定时下一次在执行判断 countdowntimer = setTimeout(countdown,options.showMillisecond ? 100 : 1000 // 毫秒级则修改定时器时间);}const data = computeCountdownInfo(remainTime, options.showMillisecond);setTimeInfo(data);}// 开始倒计时countdown();return () => {// 清除定时器timer && clearTimeout(timer);};}, [options.deadlineTime, options.showMillisecond]);return timeInfo;
};
看一下在组件中的应用:
import { Layout, Row, Typography } from 'antd';
import React from 'react';
import { useCountdown } from './CountDown';interface Props {name: string;
}const Guide: React.FC<Props> = (props) => {// deadlineTime这个时间在传的时候要比当前的时间往后,要不然效果出不来,这里注意一点const {day,hoursStr,minutesStr,secondsStr,} = useCountdown({showMillisecond: false, deadlineTime: 1743907169000})return (<Layout><div style={{color: 'red', fontSize: 28, marginBottom: 24}}>{day}天:{hoursStr}时:{minutesStr}分:{secondsStr}秒</div></Layout>);
};export default Guide;
再来看一下页面的效果吧: