139 lines
4.1 KiB
TypeScript
139 lines
4.1 KiB
TypeScript
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 = <TimerCallback[]>[];
|
||
/** 完成监听器列表,倒计时结束时触发,参数为结束时间 */
|
||
countdownEventListenerList = <TimerCallback[]>[];
|
||
|
||
/**
|
||
* @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));
|
||
}
|
||
}
|