用React实现一个秒杀倒计时组件

复制代码
先声明一个补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;

再来看一下页面的效果吧:

相关推荐
山楂树の30 分钟前
图像标注大坑:img图片 + Canvas 叠加标注,同步放大后标注位置偏移、对不齐?详解修复方案及亚像素处理原理
前端·css·学习·canva可画
本山德彪32 分钟前
我做了一个拼豆图纸生成器,把照片秒变图纸
前端
DTrader1 小时前
用TS无法实盘量化? - 实盘均线策略
前端·api
进击的夸父1 小时前
vfojs:Vue 超集架构,外壳React灵魂Vue
前端
编程老船长1 小时前
解决不同项目需要不同 Node.js 版本的问题
前端·vue.js
Wect1 小时前
LeetCode 5. 最长回文子串:DP + 中心扩展
前端·算法·typescript
漫游的渔夫1 小时前
前端开发者做 Agent:别写成一次请求,用 5 步受控循环防止 AI 乱跑
前端·人工智能·typescript
薛定猫AI2 小时前
【深度解析】Gemma Chat 本地 AI 编程 Agent:Electron + MLX + 开源模型的离线 Vibe Coding 实战
javascript·人工智能·electron
kyriewen2 小时前
Webpack vs Vite:一个是“老黄牛”,一个是“猎豹”,你选谁?
前端·webpack·vite
打小就很皮...3 小时前
html2canvas + jsPDF 生成 PDF 的踩坑与解决方案总结
前端·pdf