论巨多计时器时,页面自我救赎之路
场景
一个页面存在多个倒计时功能,或者一个项目中存在多个计时同步功能.
一般搞活动的产品贼勾八喜欢这玩意
Vue撸起
搞起
- 首先定义个函数,其中实现
倒计时+cancel倒计时+添加目标时间段+删除目标时间段+更新视图功能
定义一个 overallCoreTimerMeter 类,使用window.requestAnimationFrame进行ui重绘执行,setTimeout进行兜底。
class overallCoreTimerMeter {
private static instance: overallCoreTimerMeter;
/**
* 全局懒加载单例
*/
public static getInstance(endTime?: Time) {
if (!overallCoreTimerMeter.instance) {
overallCoreTimerMeter.instance = new overallCoreTimerMeter(endTime);
}
return overallCoreTimerMeter.instance;
}
/**
* 需要倒计时的集合
*/
list: TimeUkeyItem[] = [];
/**
* 中央计数器的结束时间
*/
endTime?: number;
/**
* 计时器的return值
*/
cancelNumber?: number;
constructor(endTime?: Time) {
// 判断是否有自动结束功能
endTime && !dayjs(endTime).isValid() && (endTime = undefined);
this.endTime = endTime ? dayjs(endTime).valueOf() : (endTime as undefined);
// 兼容
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = setTimeout;
window.cancelAnimationFrame = clearTimeout;
}
}
}
- 新增目标时间节点功能,对于目标时间节点默认转化成时间戳,方便下面的计算操作
/**
* 新增倒计时
*/
addMeter(item: AfferentTime) {
if (dayjs(item.endTime).isValid() && dayjs(item.endTime).valueOf() > Date.now()) {
item.endTime = dayjs(item.endTime).valueOf();
// 进行排序,从时间从小到大排序
this.list.push(item);
const itemCtn = item as TimeUkeyItem;
// 默认上一次的区间值
itemCtn.beforeTime = Date.now();
// 设置默认时间间隔
if (typeof itemCtn.rate !== "number") {
itemCtn.rate = 1000;
}
// 开始计时器 (从0到1)
if (this.list.length === 1) this.startTimerMeterControl();
return new Promise((resolve) => (itemCtn._resolve = resolve));
}
throw new Error("请传入key,符合时间的对象");
}
- 删除目标节点功能,对于达到目标节点去除
/**
* 删除已经达到预期时间
*/
private deleteItem(currtTime: number) {
const { list } = this;
this.list = list.reduce<TimeUkeyItem[]>((reduce, item) => {
if (item.endTime <= currtTime) {
// 删除前调用callback
item.callback?.();
item._resolve?.();
return reduce;
}
return reduce.concat(item);
}, []);
}
4.更新视图
/**
* 获取需要更新值的list
*/
private getUpdateList(currtTime: number) {
this.list.forEach((item) => {
if (item.rate === 0 || currtTime - item.beforeTime! >= item.rate!) {
// 更新了
item.beforeTime = currtTime;
this.updateTheView(currtTime, item);
}
});
}
/**
* 更新视图
*/
private updateTheView(currtTime: number, item: TimeUkeyItem) {
// 相差的时间集合
const list = timeGapNumber((item.endTime as number) - currtTime);
// 更新
item.viewRefs.value = list;
}
5.设置启动中央计时器功能,其中启动删除的控制功能在内容消化
/**
* 时间计时器
*/
private startTimerMeterControl() {
if (!this.list.length) {
this.cancelAccount();
}
// 重置上次的,新增
window.cancelAnimationFrame(this.cancelNumber!);
// 开始循环逻辑内容
const currtTime = Date.now();
// 更新视图
this.getUpdateList(currtTime);
// 删除过期的
this.deleteItem(currtTime);
// 判断中央计时器是否结束
if (this.endTime && currtTime >= this.endTime) {
this.cancelAccount();
return;
}
this.cancelNumber = window.requestAnimationFrame(this.startTimerMeterControl);
}