type TimerCallback = (time: number) => void; /** * 超时定时器 * * 基于数值的倒计时器,从 startTime 递减到 endTime, * 每隔 interval 毫秒触发步骤监听器,到达 endTime 时触发完成监听器。 * * 与 Countdown 不同的是,TimeoutTimer 使用数值而非 dayjs duration, * 适合不需要格式化输出的纯数值倒计时场景。 * * @example * ```ts * // 实例方式 * const timer = new TimeoutTimer(10, 0, 1000) * timer.addStepEventListener((time) => console.log('剩余:', time)) * timer.addCountdownEventListener((time) => console.log('结束:', time)) * timer.start() * * // 静态工厂方式 * const timer2 = TimeoutTimer.start(10, 0, 1000, * (time) => console.log('剩余:', time), * (time) => console.log('结束:', time), * ) * ``` */ export default class TimeoutTimer { /** 起始时间(数值) */ startTime = 0; /** 结束时间(数值),倒计时到此值停止 */ endTime = 0; /** 递减间隔(毫秒) */ interval = 0; /** 当前剩余时间 */ time = 0; /** 定时器 ID,-1 表示未运行 */ id = -1; /** 步骤监听器列表,每次递减时触发,参数为当前剩余时间 */ stepEventListenerList = []; /** 完成监听器列表,倒计时结束时触发,参数为结束时间 */ countdownEventListenerList = []; /** * @param startTime 起始时间(数值) * @param endTime 结束时间(数值),默认 0 * @param interval 递减间隔(毫秒),默认 1000 */ constructor(startTime = 5, endTime = 0, interval = 1000) { this.startTime = startTime; this.endTime = endTime; this.interval = interval; } /** * 添加步骤监听器 * * 每次递减时触发,参数为当前剩余时间 * * @param cb 步骤回调函数,参数为当前时间(数值) * @returns 若 cb 不是函数则返回 TypeError */ addStepEventListener(cb: TimerCallback) { if (!(cb instanceof Function)) { return new TypeError("回调函数类型错误: cb: " + cb); } this.stepEventListenerList.push(cb); } /** * 添加完成监听器 * * 倒计时到达 endTime 时触发,参数为结束时间 * * @param cb 完成回调函数,参数为结束时间(数值) * @returns 若 cb 不是函数则返回 TypeError */ addCountdownEventListener(cb: TimerCallback) { if (!(cb instanceof Function)) { return new TypeError("回调函数类型错误: cb: " + cb); } this.countdownEventListenerList.push(cb); } /** * 静态工厂方法,创建并配置超时定时器 * * @param startTime 起始时间(数值) * @param endTime 结束时间(数值),默认 0 * @param interval 递减间隔(毫秒),默认 1000 * @param stepEventListener 步骤监听器(可选) * @param countdownEventListener 完成监听器(可选) * @returns 配置好的 TimeoutTimer 实例(需手动调用 start) */ static start( startTime: number, endTime: number = 0, interval = 1000, stepEventListener?: TimerCallback, countdownEventListener?: TimerCallback, ) { const timer = new TimeoutTimer(startTime, endTime, interval); if (stepEventListener != null) { timer.addStepEventListener(stepEventListener); } if (countdownEventListener != null) { timer.addCountdownEventListener(countdownEventListener); } return timer; } /** * 开始倒计时,从 startTime 递减到 endTime,到达后自动停止 */ start() { this.time = this.startTime; this.id = window.setInterval(() => { this.time -= this.interval; console.log(this.time); this.stepEventListenerList.forEach((cb) => cb(this.time)); if (this.time <= this.endTime && this.id !== -1) { this.stop(); } }, this.interval); } /** * 停止倒计时,清除定时器并触发完成监听器 */ stop() { console.log("stop", this.id); clearInterval(this.id); this.id = -1; this.time = this.endTime; this.countdownEventListenerList.forEach((cb) => cb(this.time)); } }