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

JXDN
• 阅读 395

由于定时器精准度问题,根据设备性能的影响误差会存在 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;
点赞
收藏
评论区
推荐文章
Easter79 Easter79
3年前
Vue 全家桶
vue全家桶。使用过vue的程序员一般这样评价它,“vue.js兼具angular.js和react.js的优点”。Vue.js是一个JavaScriptMVVM(ModelViewViewModel)库,用于渐近式构建用户界面。它以数据驱动和组件化思想构建,采用自底向上增量开发的设计思想。相比Angular.js,Vue.jsAPI更加简洁;
Alex799 Alex799
3年前
Angular和Vue.js 深度对比
Vue.js是开源的JavaScript框架,能够帮助开发者构建出美观的Web界面。当和其它网络工具配合使用时,Vue.js的优秀功能会得到大大加强。如今,已有许多开发人员开始使用Vue.js来取代 Angular和React.j
Wesley13 Wesley13
3年前
java线程
倒计时器CountDownLatch使用个例publicclassTest{staticfinalCountDownLatchendnewCountDownLatch(10);staticclassCounDownLatchDemo
子桓 子桓
1年前
JavaScript开发工具:WebStorm for Mac v2023.1
WebStorm是由JetBrains公司开发的一款专业的JavaScript开发工具,它支持多种前端开发技术和框架,包括HTML、CSS、JavaScript、TypeScript、React、Angular、Vue等等。它具有丰富的功能和直观的用户界面
Easter79 Easter79
3年前
Vue 全家桶、原理及优化简议
不少互联网公司都在使用vue技术栈,或称为vue全家桶。使用过vue的程序员一般这样评价它,“vue.js兼具angular.js和react.js的优点”。Vue.js是一个JavaScriptMVVM(ModelViewViewModel)库,用于渐近式构建用户界面。它以数据驱动和组件化思想构建,采用自底向上增量开发的设计
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
铁扇公主 铁扇公主
1年前
JavaScript开发工具哪款好用?集成开发环境软件分享~
集成开发环境软件分享WebStorm中文版给大家,支持各种框架和技术,包括React、Angular、Vue.js、Node.js等,并提供了强大的代码编辑器、调试器、版本控制工具等,使得开发过程更加高效和流畅。框架和技术更新了项目模板我们重做了WebSt
JXDN
JXDN
Lv1
男 · 前后左右上中下端工程师
路虽远行则将至,事虽难做则必成
文章
2
粉丝
0
获赞
0