由于定时器精准度问题,根据设备性能的影响误差会存在 0~20ms ,所以需要结合自身的使用场景来决定能不能使用。
此处实现使用的是 setInterval 定时器加上 performance.now() 来校准 setInterval 的误差。
以下例举出了适用于各个框架的版本,可根据需求自行修改。
Vue版本
1import { reactive } from 'vue'; 2 3export interface TimeStr { 4 m : string; 5 s : string; 6 ms : string; 7} 8 9export const state = reactive<{ 10 timer : NodeJS.Timeout | null; 11 time : TimeStr; 12}>({ 13 timer: null, 14 time: { 15 m: '00', 16 s: '00', 17 ms: '000' 18 } 19}); 20 21/** 22 * 获取高精度当前时间戳 23 */ 24const getCurrentTime = () => { 25 return performance.now(); 26}; 27 28/** 29 * 计算时间差 30 * @param startTime 开始时间戳 31 * @param endTime 结束时间戳 32 * @param countTime 总时长 33 */ 34const diffTime = (startTime : number, endTime : number, countTime : number) : TimeStr => { 35 const calculateTime = (time : number) => { 36 const totalSeconds = time / 1000; 37 const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0'); 38 const seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0'); 39 const milliseconds = Math.round((totalSeconds % 1) * 1000).toString().padStart(3, '0'); 40 return { m: minutes, s: seconds, ms: milliseconds }; 41 }; 42 43 const duration = endTime - startTime; 44 45 if (duration >= countTime) { 46 return calculateTime(countTime); 47 } 48 49 return calculateTime(duration); 50}; 51 52/** 53 * 开始计时 54 * @param limitTime 可选的计时时间(毫秒) 55 * @param callback 可选的计时结束回调函数 56 */ 57export const timing = (limitTime ?: number, callback ?: () => void) => { 58 if (state.timer) { 59 clearInterval(state.timer); 60 } 61 62 const startTime = getCurrentTime(); 63 64 state.timer = setInterval(() => { 65 const currentTime = getCurrentTime(); 66 const elapsed = currentTime - startTime; 67 68 if (limitTime !== undefined && elapsed >= limitTime) { 69 stop(() => { 70 callback && callback(); 71 console.log('计时已结束'); 72 }); 73 } else { 74 const diff = limitTime !== undefined ? diffTime(startTime, currentTime, limitTime) : diffTime(startTime, currentTime, Number.MAX_SAFE_INTEGER); 75 state.time = diff; 76 } 77 }, 1); 78}; 79 80/** 81 * 开始倒计时 82 * @param countdownTime 倒计时时间(毫秒) 83 * @param callback 倒计时结束回调 84 */ 85export const down = (countdownTime : number, callback ?: () => void) => { 86 // 设置初始倒计时 87 state.time = diffTime(0, countdownTime, countdownTime); 88 89 if (state.timer) { 90 clearInterval(state.timer); 91 } 92 93 const startTime = getCurrentTime(); 94 95 state.timer = setInterval(() => { 96 const currentTime = getCurrentTime(); 97 const elapsed = currentTime - startTime; 98 const remainingTime = countdownTime - elapsed; 99 100 if (remainingTime <= 0) { 101 stop(() => { 102 state.time = { m: '00', s: '00', ms: '000' }; 103 callback && callback(); 104 console.log('倒计时已结束'); 105 }); 106 } else { 107 state.time = diffTime(0, remainingTime, countdownTime); 108 } 109 }, 1); 110}; 111 112/** 113 * 重置计时器 114 */ 115export const reset = (callback ?: () => void) => { 116 if (state.timer) { 117 clearInterval(state.timer); 118 state.timer = null; 119 } 120 state.time = { m: '00', s: '00', ms: '000' }; 121 callback && callback() 122 console.log('计时器已重置'); 123}; 124 125/** 126 * 停止计时 127 * @param callback 停止计时回调函数 128 */ 129export const stop = (callback ?: (time : TimeStr) => void) => { 130 if (state.timer) { 131 clearInterval(state.timer); 132 state.timer = null; 133 callback && callback(state.time); 134 } 135};
React版本
1import React, { useState, useRef, useEffect } from 'react'; 2 3export interface TimeStr { 4 m: string; 5 s: string; 6 ms: string; 7} 8 9const useTimer = () => { 10 const [time, setTime] = useState<TimeStr>({ m: '00', s: '00', ms: '000' }); 11 const timerRef = useRef<NodeJS.Timeout | null>(null); 12 13 const getCurrentTime = () => performance.now(); 14 15 const diffTime = (startTime: number, endTime: number, countTime: number): TimeStr => { 16 const calculateTime = (time: number) => { 17 const totalSeconds = time / 1000; 18 const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0'); 19 const seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0'); 20 const milliseconds = Math.round((totalSeconds % 1) * 1000).toString().padStart(3, '0'); 21 return { m: minutes, s: seconds, ms: milliseconds }; 22 }; 23 24 const duration = endTime - startTime; 25 26 if (duration >= countTime) { 27 return calculateTime(countTime); 28 } 29 30 return calculateTime(duration); 31 }; 32 33 const timing = (limitTime?: number, callback?: () => void) => { 34 if (timerRef.current) { 35 clearInterval(timerRef.current); 36 } 37 38 const startTime = getCurrentTime(); 39 40 timerRef.current = setInterval(() => { 41 const currentTime = getCurrentTime(); 42 const elapsed = currentTime - startTime; 43 44 if (limitTime !== undefined && elapsed >= limitTime) { 45 stop(() => { 46 callback && callback(); 47 console.log('计时已结束'); 48 }); 49 } else { 50 const diff = limitTime !== undefined ? diffTime(startTime, currentTime, limitTime) : diffTime(startTime, currentTime, Number.MAX_SAFE_INTEGER); 51 setTime(diff); 52 } 53 }, 1); 54 }; 55 56 const down = (countdownTime: number, callback?: () => void) => { 57 setTime(diffTime(0, countdownTime, countdownTime)); 58 59 if (timerRef.current) { 60 clearInterval(timerRef.current); 61 } 62 63 const startTime = getCurrentTime(); 64 65 timerRef.current = setInterval(() => { 66 const currentTime = getCurrentTime(); 67 const elapsed = currentTime - startTime; 68 const remainingTime = countdownTime - elapsed; 69 70 if (remainingTime <= 0) { 71 stop(() => { 72 setTime({ m: '00', s: '00', ms: '000' }); 73 callback && callback(); 74 console.log('倒计时已结束'); 75 }); 76 } else { 77 setTime(diffTime(0, remainingTime, countdownTime)); 78 } 79 }, 1); 80 }; 81 82 const reset = (callback?: () => void) => { 83 if (timerRef.current) { 84 clearInterval(timerRef.current); 85 timerRef.current = null; 86 } 87 setTime({ m: '00', s: '00', ms: '000' }); 88 callback && callback(); 89 console.log('计时器已重置'); 90 }; 91 92 const stop = (callback?: (time: TimeStr) => void) => { 93 if (timerRef.current) { 94 clearInterval(timerRef.current); 95 timerRef.current = null; 96 callback && callback(time); 97 } 98 }; 99 100 return { time, timing, down, reset, stop }; 101}; 102 103export default useTimer;
Angular版本
1import { Injectable } from '@angular/core'; 2 3export interface TimeStr { 4 m: string; 5 s: string; 6 ms: string; 7} 8 9@Injectable({ 10 providedIn: 'root' 11}) 12export class TimerService { 13 time: TimeStr = { m: '00', s: '00', ms: '000' }; 14 timer: any = null; 15 16 getCurrentTime(): number { 17 return performance.now(); 18 } 19 20 diffTime(startTime: number, endTime: number, countTime: number): TimeStr { 21 const calculateTime = (time: number) => { 22 const totalSeconds = time / 1000; 23 const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0'); 24 const seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0'); 25 const milliseconds = Math.round((totalSeconds % 1) * 1000).toString().padStart(3, '0'); 26 return { m: minutes, s: seconds, ms: milliseconds }; 27 }; 28 29 const duration = endTime - startTime; 30 31 if (duration >= countTime) { 32 return calculateTime(countTime); 33 } 34 35 return calculateTime(duration); 36 } 37 38 timing(limitTime?: number, callback?: () => void): void { 39 if (this.timer) { 40 clearInterval(this.timer); 41 } 42 43 const startTime = this.getCurrentTime(); 44 45 this.timer = setInterval(() => { 46 const currentTime = this.getCurrentTime(); 47 const elapsed = currentTime - startTime; 48 49 if (limitTime !== undefined && elapsed >= limitTime) { 50 this.stop(() => { 51 callback && callback(); 52 console.log('计时已结束'); 53 }); 54 } else { 55 const diff = limitTime !== undefined ? this.diffTime(startTime, currentTime, limitTime) : this.diffTime(startTime, currentTime, Number.MAX_SAFE_INTEGER); 56 this.time = diff; 57 } 58 }, 1); 59 } 60 61 down(countdownTime: number, callback?: () => void): void { 62 this.time = this.diffTime(0, countdownTime, countdownTime); 63 64 if (this.timer) { 65 clearInterval(this.timer); 66 } 67 68 const startTime = this.getCurrentTime(); 69 70 this.timer = setInterval(() => { 71 const currentTime = this.getCurrentTime(); 72 const elapsed = currentTime - startTime; 73 const remainingTime = countdownTime - elapsed; 74 75 if (remainingTime <= 0) { 76 this.stop(() => { 77 this.time = { m: '00', s: '00', ms: '000' }; 78 callback && callback(); 79 console.log('倒计时已结束'); 80 }); 81 } else { 82 this.time = this.diffTime(0, remainingTime, countdownTime); 83 } 84 }, 1); 85 } 86 87 reset(callback?: () => void): void { 88 if (this.timer) { 89 clearInterval(this.timer); 90 this.timer = null; 91 } 92 this.time = { m: '00', s: '00', ms: '000' }; 93 callback && callback(); 94 console.log('计时器已重置'); 95 } 96 97 stop(callback?: (time: TimeStr) => void): void { 98 if (this.timer) { 99 clearInterval(this.timer); 100 this.timer = null; 101 callback && callback(this.time); 102 } 103 } 104}
纯Typescript版本
1export interface TimeStr { 2 m: string; 3 s: string; 4 ms: string; 5} 6 7class Timer { 8 time: TimeStr = { m: '00', s: '00', ms: '000' }; 9 timer: NodeJS.Timeout | null = null; 10 11 getCurrentTime(): number { 12 return performance.now(); 13 } 14 15 diffTime(startTime: number, endTime: number, countTime: number): TimeStr { 16 const calculateTime = (time: number) => { 17 const totalSeconds = time / 1000; 18 const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0'); 19 const seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0'); 20 const milliseconds = Math.round((totalSeconds % 1) * 1000).toString().padStart(3, '0'); 21 return { m: minutes, s: seconds, ms: milliseconds }; 22 }; 23 24 const duration = endTime - startTime; 25 26 if (duration >= countTime) { 27 return calculateTime(countTime); 28 } 29 30 return calculateTime(duration); 31 } 32 33 timing(limitTime?: number, callback?: () => void): void { 34 if (this.timer) { 35 clearInterval(this.timer); 36 } 37 38 const startTime = this.getCurrentTime(); 39 40 this.timer = setInterval(() => { 41 const currentTime = this.getCurrentTime(); 42 const elapsed = currentTime - startTime; 43 44 if (limitTime !== undefined && elapsed >= limitTime) { 45 this.stop(() => { 46 callback && callback(); 47 console.log('计时已结束'); 48 }); 49 } else { 50 const diff = limitTime !== undefined ? this.diffTime(startTime, currentTime, limitTime) : this.diffTime(startTime, currentTime, Number.MAX_SAFE_INTEGER); 51 this.time = diff; 52 } 53 }, 1); 54 } 55 56 down(countdownTime: number, callback?: () => void): void { 57 this.time = this.diffTime(0, countdownTime, countdownTime); 58 59 if (this.timer) { 60 clearInterval(this.timer); 61 } 62 63 const startTime = this.getCurrentTime(); 64 65 this.timer = setInterval(() => { 66 const currentTime = this.getCurrentTime(); 67 const elapsed = currentTime - startTime; 68 const remainingTime = countdownTime - elapsed; 69 70 if (remainingTime <= 0) { 71 this.stop(() => { 72 this.time = { m: '00', s: '00', ms: '000' }; 73 callback && callback(); 74 console.log('倒计时已结束'); 75 }); 76 } else { 77 this.time = this.diffTime(0, remainingTime, countdownTime); 78 } 79 }, 1); 80 } 81 82 reset(callback?: () => void): void { 83 if (this.timer) { 84 clearInterval(this.timer); 85 this.timer = null; 86 } 87 this.time = { m: '00', s: '00', ms: '000' }; 88 callback && callback(); 89 console.log('计时器已重置'); 90 } 91 92 stop(callback?: (time: TimeStr) => void): void { 93 if (this.timer) { 94 clearInterval(this.timer); 95 this.timer = null; 96 callback && callback(this.time); 97 } 98 } 99} 100 101export default Timer;
纯Js版本
1class Timer { 2 constructor() { 3 this.time = { m: '00', s: '00', ms: '000' }; 4 this.timer = null; 5 } 6 7 getCurrentTime() { 8 return performance.now(); 9 } 10 11 diffTime(startTime, endTime, countTime) { 12 const calculateTime = (time) => { 13 const totalSeconds = time / 1000; 14 const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0'); 15 const seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0'); 16 const milliseconds = Math.round((totalSeconds % 1) * 1000).toString().padStart(3, '0'); 17 return { m: minutes, s: seconds, ms: milliseconds }; 18 }; 19 20 const duration = endTime - startTime; 21 22 if (duration >= countTime) { 23 return calculateTime(countTime); 24 } 25 26 return calculateTime(duration); 27 } 28 29 timing(limitTime, callback) { 30 if (this.timer) { 31 clearInterval(this.timer); 32 } 33 34 const startTime = this.getCurrentTime(); 35 36 this.timer = setInterval(() => { 37 const currentTime = this.getCurrentTime(); 38 const elapsed = currentTime - startTime; 39 40 if (limitTime !== undefined && elapsed >= limitTime) { 41 this.stop(() => { 42 if (callback) callback(); 43 console.log('计时已结束'); 44 }); 45 } else { 46 const diff = limitTime !== undefined ? this.diffTime(startTime, currentTime, limitTime) : this.diffTime(startTime, currentTime, Number.MAX_SAFE_INTEGER); 47 this.time = diff; 48 } 49 }, 1); 50 } 51 52 down(countdownTime, callback) { 53 this.time = this.diffTime(0, countdownTime, countdownTime); 54 55 if (this.timer) { 56 clearInterval(this.timer); 57 } 58 59 const startTime = this.getCurrentTime(); 60 61 this.timer = setInterval(() => { 62 const currentTime = this.getCurrentTime(); 63 const elapsed = currentTime - startTime; 64 const remainingTime = countdownTime - elapsed; 65 66 if (remainingTime <= 0) { 67 this.stop(() => { 68 this.time = { m: '00', s: '00', ms: '000' }; 69 if (callback) callback(); 70 console.log('倒计时已结束'); 71 }); 72 } else { 73 this.time = this.diffTime(0, remainingTime, countdownTime); 74 } 75 }, 1); 76 } 77 78 reset(callback) { 79 if (this.timer) { 80 clearInterval(this.timer); 81 this.timer = null; 82 } 83 this.time = { m: '00', s: '00', ms: '000' }; 84 if (callback) callback(); 85 console.log('计时器已重置'); 86 } 87 88 stop(callback) { 89 if (this.timer) { 90 clearInterval(this.timer); 91 this.timer = null; 92 if (callback) callback(this.time); 93 } 94 } 95} 96 97export default Timer;
