适用于Vue、React、Angular、Ts和Js毫秒级计时器,包括正、倒计时

由于定时器精准度问题,根据设备性能的影响误差会存在 0~20ms ,所以需要结合自身的使用场景来决定能不能使用。

此处实现使用的是 setInterval 定时器加上 performance.now() 来校准 setInterval 的误差。

以下例举出了适用于各个框架的版本,可根据需求自行修改。

Vue版本
import { reactive } from 'vue';

export interface TimeStr {
	m : string;
	s : string;
	ms : string;
}

export const state = reactive<{
	timer : NodeJS.Timeout | null;
	time : TimeStr;
}>({
	timer: null,
	time: {
		m: '00',
		s: '00',
		ms: '000'
	}
});

/**
 * 获取高精度当前时间戳
 */
const getCurrentTime = () => {
	return performance.now();
};

/**
 * 计算时间差
 * @param startTime 开始时间戳
 * @param endTime 结束时间戳
 * @param countTime 总时长
 */
const diffTime = (startTime : number, endTime : number, countTime : number) : TimeStr => {
	const calculateTime = (time : number) => {
		const totalSeconds = time / 1000;
		const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0');
		const seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0');
		const milliseconds = Math.round((totalSeconds % 1) * 1000).toString().padStart(3, '0');
		return { m: minutes, s: seconds, ms: milliseconds };
	};

	const duration = endTime - startTime;

	if (duration >= countTime) {
		return calculateTime(countTime);
	}

	return calculateTime(duration);
};

/**
 * 开始计时
 * @param limitTime 可选的计时时间(毫秒)
 * @param callback 可选的计时结束回调函数
 */
export const timing = (limitTime ?: number, callback ?: () => void) => {
	if (state.timer) {
		clearInterval(state.timer);
	}

	const startTime = getCurrentTime();

	state.timer = setInterval(() => {
		const currentTime = getCurrentTime();
		const elapsed = currentTime - startTime;

		if (limitTime !== undefined && elapsed >= limitTime) {
			stop(() => {
				callback && callback();
				console.log('计时已结束');
			});
		} else {
			const diff = limitTime !== undefined ? diffTime(startTime, currentTime, limitTime) : diffTime(startTime, currentTime, Number.MAX_SAFE_INTEGER);
			state.time = diff;
		}
	}, 1);
};

/**
 * 开始倒计时
 * @param countdownTime 倒计时时间(毫秒)
 * @param callback 倒计时结束回调
 */
export const down = (countdownTime : number, callback ?: () => void) => {
	// 设置初始倒计时
	state.time = diffTime(0, countdownTime, countdownTime);

	if (state.timer) {
		clearInterval(state.timer);
	}

	const startTime = getCurrentTime();

	state.timer = setInterval(() => {
		const currentTime = getCurrentTime();
		const elapsed = currentTime - startTime;
		const remainingTime = countdownTime - elapsed;

		if (remainingTime <= 0) {
			stop(() => {
				state.time = { m: '00', s: '00', ms: '000' };
				callback && callback();
				console.log('倒计时已结束');
			});
		} else {
			state.time = diffTime(0, remainingTime, countdownTime);
		}
	}, 1);
};

/**
 * 重置计时器
 */
export const reset = (callback ?: () => void) => {
	if (state.timer) {
		clearInterval(state.timer);
		state.timer = null;
	}
	state.time = { m: '00', s: '00', ms: '000' };
	callback && callback()
	console.log('计时器已重置');
};

/**
 * 停止计时
 * @param callback 停止计时回调函数
 */
export const stop = (callback ?: (time : TimeStr) => void) => {
	if (state.timer) {
		clearInterval(state.timer);
		state.timer = null;
		callback && callback(state.time);
	}
};
React版本
import React, { useState, useRef, useEffect } from 'react';

export interface TimeStr {
    m: string;
    s: string;
    ms: string;
}

const useTimer = () => {
    const [time, setTime] = useState<TimeStr>({ m: '00', s: '00', ms: '000' });
    const timerRef = useRef<NodeJS.Timeout | null>(null);

    const getCurrentTime = () => performance.now();

    const diffTime = (startTime: number, endTime: number, countTime: number): TimeStr => {
        const calculateTime = (time: number) => {
            const totalSeconds = time / 1000;
            const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0');
            const seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0');
            const milliseconds = Math.round((totalSeconds % 1) * 1000).toString().padStart(3, '0');
            return { m: minutes, s: seconds, ms: milliseconds };
        };

        const duration = endTime - startTime;

        if (duration >= countTime) {
            return calculateTime(countTime);
        }

        return calculateTime(duration);
    };

    const timing = (limitTime?: number, callback?: () => void) => {
        if (timerRef.current) {
            clearInterval(timerRef.current);
        }

        const startTime = getCurrentTime();

        timerRef.current = setInterval(() => {
            const currentTime = getCurrentTime();
            const elapsed = currentTime - startTime;

            if (limitTime !== undefined && elapsed >= limitTime) {
                stop(() => {
                    callback && callback();
                    console.log('计时已结束');
                });
            } else {
                const diff = limitTime !== undefined ? diffTime(startTime, currentTime, limitTime) : diffTime(startTime, currentTime, Number.MAX_SAFE_INTEGER);
                setTime(diff);
            }
        }, 1);
    };

    const down = (countdownTime: number, callback?: () => void) => {
        setTime(diffTime(0, countdownTime, countdownTime));

        if (timerRef.current) {
            clearInterval(timerRef.current);
        }

        const startTime = getCurrentTime();

        timerRef.current = setInterval(() => {
            const currentTime = getCurrentTime();
            const elapsed = currentTime - startTime;
            const remainingTime = countdownTime - elapsed;

            if (remainingTime <= 0) {
                stop(() => {
                    setTime({ m: '00', s: '00', ms: '000' });
                    callback && callback();
                    console.log('倒计时已结束');
                });
            } else {
                setTime(diffTime(0, remainingTime, countdownTime));
            }
        }, 1);
    };

    const reset = (callback?: () => void) => {
        if (timerRef.current) {
            clearInterval(timerRef.current);
            timerRef.current = null;
        }
        setTime({ m: '00', s: '00', ms: '000' });
        callback && callback();
        console.log('计时器已重置');
    };

    const stop = (callback?: (time: TimeStr) => void) => {
        if (timerRef.current) {
            clearInterval(timerRef.current);
            timerRef.current = null;
            callback && callback(time);
        }
    };

    return { time, timing, down, reset, stop };
};

export default useTimer;
Angular版本
import { Injectable } from '@angular/core';

export interface TimeStr {
  m: string;
  s: string;
  ms: string;
}

@Injectable({
  providedIn: 'root'
})
export class TimerService {
  time: TimeStr = { m: '00', s: '00', ms: '000' };
  timer: any = null;

  getCurrentTime(): number {
    return performance.now();
  }

  diffTime(startTime: number, endTime: number, countTime: number): TimeStr {
    const calculateTime = (time: number) => {
      const totalSeconds = time / 1000;
      const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0');
      const seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0');
      const milliseconds = Math.round((totalSeconds % 1) * 1000).toString().padStart(3, '0');
      return { m: minutes, s: seconds, ms: milliseconds };
    };

    const duration = endTime - startTime;

    if (duration >= countTime) {
      return calculateTime(countTime);
    }

    return calculateTime(duration);
  }

  timing(limitTime?: number, callback?: () => void): void {
    if (this.timer) {
      clearInterval(this.timer);
    }

    const startTime = this.getCurrentTime();

    this.timer = setInterval(() => {
      const currentTime = this.getCurrentTime();
      const elapsed = currentTime - startTime;

      if (limitTime !== undefined && elapsed >= limitTime) {
        this.stop(() => {
          callback && callback();
          console.log('计时已结束');
        });
      } else {
        const diff = limitTime !== undefined ? this.diffTime(startTime, currentTime, limitTime) : this.diffTime(startTime, currentTime, Number.MAX_SAFE_INTEGER);
        this.time = diff;
      }
    }, 1);
  }

  down(countdownTime: number, callback?: () => void): void {
    this.time = this.diffTime(0, countdownTime, countdownTime);

    if (this.timer) {
      clearInterval(this.timer);
    }

    const startTime = this.getCurrentTime();

    this.timer = setInterval(() => {
      const currentTime = this.getCurrentTime();
      const elapsed = currentTime - startTime;
      const remainingTime = countdownTime - elapsed;

      if (remainingTime <= 0) {
        this.stop(() => {
          this.time = { m: '00', s: '00', ms: '000' };
          callback && callback();
          console.log('倒计时已结束');
        });
      } else {
        this.time = this.diffTime(0, remainingTime, countdownTime);
      }
    }, 1);
  }

  reset(callback?: () => void): void {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }
    this.time = { m: '00', s: '00', ms: '000' };
    callback && callback();
    console.log('计时器已重置');
  }

  stop(callback?: (time: TimeStr) => void): void {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
      callback && callback(this.time);
    }
  }
}
纯Typescript版本
export interface TimeStr {
    m: string;
    s: string;
    ms: string;
}

class Timer {
    time: TimeStr = { m: '00', s: '00', ms: '000' };
    timer: NodeJS.Timeout | null = null;

    getCurrentTime(): number {
        return performance.now();
    }

    diffTime(startTime: number, endTime: number, countTime: number): TimeStr {
        const calculateTime = (time: number) => {
            const totalSeconds = time / 1000;
            const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0');
            const seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0');
            const milliseconds = Math.round((totalSeconds % 1) * 1000).toString().padStart(3, '0');
            return { m: minutes, s: seconds, ms: milliseconds };
        };

        const duration = endTime - startTime;

        if (duration >= countTime) {
            return calculateTime(countTime);
        }

        return calculateTime(duration);
    }

    timing(limitTime?: number, callback?: () => void): void {
        if (this.timer) {
            clearInterval(this.timer);
        }

        const startTime = this.getCurrentTime();

        this.timer = setInterval(() => {
            const currentTime = this.getCurrentTime();
            const elapsed = currentTime - startTime;

            if (limitTime !== undefined && elapsed >= limitTime) {
                this.stop(() => {
                    callback && callback();
                    console.log('计时已结束');
                });
            } else {
                const diff = limitTime !== undefined ? this.diffTime(startTime, currentTime, limitTime) : this.diffTime(startTime, currentTime, Number.MAX_SAFE_INTEGER);
                this.time = diff;
            }
        }, 1);
    }

    down(countdownTime: number, callback?: () => void): void {
        this.time = this.diffTime(0, countdownTime, countdownTime);

        if (this.timer) {
            clearInterval(this.timer);
        }

        const startTime = this.getCurrentTime();

        this.timer = setInterval(() => {
            const currentTime = this.getCurrentTime();
            const elapsed = currentTime - startTime;
            const remainingTime = countdownTime - elapsed;

            if (remainingTime <= 0) {
                this.stop(() => {
                    this.time = { m: '00', s: '00', ms: '000' };
                    callback && callback();
                    console.log('倒计时已结束');
                });
            } else {
                this.time = this.diffTime(0, remainingTime, countdownTime);
            }
        }, 1);
    }

    reset(callback?: () => void): void {
        if (this.timer) {
            clearInterval(this.timer);
            this.timer = null;
        }
        this.time = { m: '00', s: '00', ms: '000' };
        callback && callback();
        console.log('计时器已重置');
    }

    stop(callback?: (time: TimeStr) => void): void {
        if (this.timer) {
            clearInterval(this.timer);
            this.timer = null;
            callback && callback(this.time);
        }
    }
}

export default Timer;
纯Js版本
class Timer {
    constructor() {
        this.time = { m: '00', s: '00', ms: '000' };
        this.timer = null;
    }

    getCurrentTime() {
        return performance.now();
    }

    diffTime(startTime, endTime, countTime) {
        const calculateTime = (time) => {
            const totalSeconds = time / 1000;
            const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0');
            const seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0');
            const milliseconds = Math.round((totalSeconds % 1) * 1000).toString().padStart(3, '0');
            return { m: minutes, s: seconds, ms: milliseconds };
        };

        const duration = endTime - startTime;

        if (duration >= countTime) {
            return calculateTime(countTime);
        }

        return calculateTime(duration);
    }

    timing(limitTime, callback) {
        if (this.timer) {
            clearInterval(this.timer);
        }

        const startTime = this.getCurrentTime();

        this.timer = setInterval(() => {
            const currentTime = this.getCurrentTime();
            const elapsed = currentTime - startTime;

            if (limitTime !== undefined && elapsed >= limitTime) {
                this.stop(() => {
                    if (callback) callback();
                    console.log('计时已结束');
                });
            } else {
                const diff = limitTime !== undefined ? this.diffTime(startTime, currentTime, limitTime) : this.diffTime(startTime, currentTime, Number.MAX_SAFE_INTEGER);
                this.time = diff;
            }
        }, 1);
    }

    down(countdownTime, callback) {
        this.time = this.diffTime(0, countdownTime, countdownTime);

        if (this.timer) {
            clearInterval(this.timer);
        }

        const startTime = this.getCurrentTime();

        this.timer = setInterval(() => {
            const currentTime = this.getCurrentTime();
            const elapsed = currentTime - startTime;
            const remainingTime = countdownTime - elapsed;

            if (remainingTime <= 0) {
                this.stop(() => {
                    this.time = { m: '00', s: '00', ms: '000' };
                    if (callback) callback();
                    console.log('倒计时已结束');
                });
            } else {
                this.time = this.diffTime(0, remainingTime, countdownTime);
            }
        }, 1);
    }

    reset(callback) {
        if (this.timer) {
            clearInterval(this.timer);
            this.timer = null;
        }
        this.time = { m: '00', s: '00', ms: '000' };
        if (callback) callback();
        console.log('计时器已重置');
    }

    stop(callback) {
        if (this.timer) {
            clearInterval(this.timer);
            this.timer = null;
            if (callback) callback(this.time);
        }
    }
}

export default Timer;
  • 12
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值