2026-03-16 17:58:57 +08:00
|
|
|
|
type TimerCallback = (time: number) => void;
|
|
|
|
|
|
|
2026-06-30 11:21:16 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 超时定时器
|
|
|
|
|
|
*
|
|
|
|
|
|
* 基于数值的倒计时器,从 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),
|
|
|
|
|
|
* )
|
|
|
|
|
|
* ```
|
|
|
|
|
|
*/
|
2026-03-16 17:58:57 +08:00
|
|
|
|
export default class TimeoutTimer {
|
2026-06-30 11:21:16 +08:00
|
|
|
|
/** 起始时间(数值) */
|
2026-03-16 17:58:57 +08:00
|
|
|
|
startTime = 0;
|
2026-06-30 11:21:16 +08:00
|
|
|
|
/** 结束时间(数值),倒计时到此值停止 */
|
2026-03-16 17:58:57 +08:00
|
|
|
|
endTime = 0;
|
2026-06-30 11:21:16 +08:00
|
|
|
|
/** 递减间隔(毫秒) */
|
2026-03-16 17:58:57 +08:00
|
|
|
|
interval = 0;
|
2026-06-30 11:21:16 +08:00
|
|
|
|
/** 当前剩余时间 */
|
2026-03-16 17:58:57 +08:00
|
|
|
|
time = 0;
|
2026-06-30 11:21:16 +08:00
|
|
|
|
/** 定时器 ID,-1 表示未运行 */
|
2026-03-16 17:58:57 +08:00
|
|
|
|
id = -1;
|
2026-06-30 11:21:16 +08:00
|
|
|
|
/** 步骤监听器列表,每次递减时触发,参数为当前剩余时间 */
|
2026-03-16 17:58:57 +08:00
|
|
|
|
stepEventListenerList = <TimerCallback[]>[];
|
2026-06-30 11:21:16 +08:00
|
|
|
|
/** 完成监听器列表,倒计时结束时触发,参数为结束时间 */
|
2026-03-16 17:58:57 +08:00
|
|
|
|
countdownEventListenerList = <TimerCallback[]>[];
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-06-30 11:21:16 +08:00
|
|
|
|
* @param startTime 起始时间(数值)
|
|
|
|
|
|
* @param endTime 结束时间(数值),默认 0
|
|
|
|
|
|
* @param interval 递减间隔(毫秒),默认 1000
|
2026-03-16 17:58:57 +08:00
|
|
|
|
*/
|
|
|
|
|
|
constructor(startTime = 5, endTime = 0, interval = 1000) {
|
|
|
|
|
|
this.startTime = startTime;
|
|
|
|
|
|
this.endTime = endTime;
|
|
|
|
|
|
this.interval = interval;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-06-30 11:21:16 +08:00
|
|
|
|
* 添加步骤监听器
|
|
|
|
|
|
*
|
|
|
|
|
|
* 每次递减时触发,参数为当前剩余时间
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param cb 步骤回调函数,参数为当前时间(数值)
|
|
|
|
|
|
* @returns 若 cb 不是函数则返回 TypeError
|
2026-03-16 17:58:57 +08:00
|
|
|
|
*/
|
|
|
|
|
|
addStepEventListener(cb: TimerCallback) {
|
|
|
|
|
|
if (!(cb instanceof Function)) {
|
|
|
|
|
|
return new TypeError("回调函数类型错误: cb: " + cb);
|
|
|
|
|
|
}
|
|
|
|
|
|
this.stepEventListenerList.push(cb);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-06-30 11:21:16 +08:00
|
|
|
|
* 添加完成监听器
|
|
|
|
|
|
*
|
|
|
|
|
|
* 倒计时到达 endTime 时触发,参数为结束时间
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param cb 完成回调函数,参数为结束时间(数值)
|
|
|
|
|
|
* @returns 若 cb 不是函数则返回 TypeError
|
2026-03-16 17:58:57 +08:00
|
|
|
|
*/
|
|
|
|
|
|
addCountdownEventListener(cb: TimerCallback) {
|
|
|
|
|
|
if (!(cb instanceof Function)) {
|
|
|
|
|
|
return new TypeError("回调函数类型错误: cb: " + cb);
|
|
|
|
|
|
}
|
|
|
|
|
|
this.countdownEventListenerList.push(cb);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-06-30 11:21:16 +08:00
|
|
|
|
* 静态工厂方法,创建并配置超时定时器
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param startTime 起始时间(数值)
|
|
|
|
|
|
* @param endTime 结束时间(数值),默认 0
|
|
|
|
|
|
* @param interval 递减间隔(毫秒),默认 1000
|
|
|
|
|
|
* @param stepEventListener 步骤监听器(可选)
|
|
|
|
|
|
* @param countdownEventListener 完成监听器(可选)
|
|
|
|
|
|
* @returns 配置好的 TimeoutTimer 实例(需手动调用 start)
|
2026-03-16 17:58:57 +08:00
|
|
|
|
*/
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-06-30 11:21:16 +08:00
|
|
|
|
* 开始倒计时,从 startTime 递减到 endTime,到达后自动停止
|
2026-03-16 17:58:57 +08:00
|
|
|
|
*/
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-06-30 11:21:16 +08:00
|
|
|
|
* 停止倒计时,清除定时器并触发完成监听器
|
2026-03-16 17:58:57 +08:00
|
|
|
|
*/
|
|
|
|
|
|
stop() {
|
|
|
|
|
|
console.log("stop", this.id);
|
|
|
|
|
|
|
|
|
|
|
|
clearInterval(this.id);
|
|
|
|
|
|
this.id = -1;
|
|
|
|
|
|
this.time = this.endTime;
|
|
|
|
|
|
this.countdownEventListenerList.forEach((cb) => cb(this.time));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|