-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathCountdown.ts
149 lines (130 loc) · 3.56 KB
/
Countdown.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import { tick } from './tick';
export type CountdownValueIndex = 'd' | 'h' | 'i' | 's';
export type CountdownValue = {
[key in CountdownValueIndex]?: number;
};
export type UpdateCallback = (value: CountdownValue) => void;
export interface CountdownOption {
// 倒计时剩余时间
remain?: number | string;
// 更新时触发
onUpdate?: UpdateCallback;
// 结束时触发
onEnd?: () => void;
// 格式dhis
format?: string;
}
export class Countdown {
/**
* 倒计时的剩余时间,单位为秒
*/
total = 0;
remain = 0;
/**
* 当前倒计时的格式
* d:天
* h:时
* i:分
* s:秒
*/
format = ['d', 'h', 'i', 's'];
// 逐帧tick
_stopTick?: () => void;
// 每次更新时都会调用
_onUpdate?: UpdateCallback;
// 结束时触发调用
_onEnd?: () => void;
constructor(option: CountdownOption) {
if (typeof option.remain === 'number' && option.remain >= 0) {
this.total = this.remain = option.remain;
}
// 倒计时需要展示的时间格式
if (typeof option.format === 'string') {
const parts = option.format.split('');
const output: string[] = [];
this.format.forEach((item) => {
if (parts.includes(item)) {
output.push(item);
}
});
this.format = output;
} else {
// 设置默认的倒计时格式
this.format = ['h', 'i', 's'];
}
this._onUpdate = option.onUpdate;
this._onEnd = option.onEnd;
}
onUpdate(callback: UpdateCallback) {
this._onUpdate = callback;
}
onEnd(callback: () => void) {
this._onEnd = callback;
}
start() {
// 如果倒计时时间不够,直接返回
if (this.remain <= 0) {
this._stopTick?.();
this._onUpdate?.(this.formatValue());
this._onEnd?.();
return;
}
// 初始化立即触发一次更新
this._onUpdate?.(this.formatValue());
// 记录倒计时开启时的时间
const start = Date.now();
this._stopTick = tick(() => {
// 获取倒计时已经持续的时间
const duration = Math.floor((Date.now() - start) / 1000);
const currentRemain = this.total - duration;
// 倒计时结束
if (currentRemain <= 0) {
this.remain = 0;
this._stopTick?.();
this._onUpdate?.(this.formatValue());
this._onEnd?.();
return;
}
// 调用更新,这里是防止一秒以内多次反复渲染
if (currentRemain !== this.remain) {
this.remain = currentRemain;
this._onUpdate?.(this.formatValue());
}
});
}
// 停止倒计时
stop() {
this.total = this.remain;
this._stopTick?.();
}
/**
* 格式化每次更新的值
* @param remainTime
*/
formatValue(): CountdownValue {
let remainTime = this.remain;
const result: CountdownValue = {};
this.format.forEach((key) => {
switch (key) {
case 'd':
result.d = Math.floor(remainTime / 86400);
remainTime = remainTime - result.d * 86400;
break;
case 'h':
result.h = Math.floor(remainTime / 3600);
remainTime = remainTime - result.h * 3600;
break;
case 'i':
result.i = Math.floor(remainTime / 60);
remainTime = remainTime - result.i * 60;
break;
case 's':
result.s = remainTime;
break;
default:
break;
}
});
return result;
}
}