先声明一个补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: boolean
deadlineTime: 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) {
// 未结束时直接定时下一次在执行判断 countdown
timer = 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;
再来看一下页面的效果吧:
