Last active
February 5, 2024 11:01
-
-
Save zzjtnb/79160e5dfc0b42e102c21aaa3e08d3d4 to your computer and use it in GitHub Desktop.
用本地时间差来解决由于js阻塞导致的倒计时延迟的问题
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Timer类用于创建一个倒计时器 | |
* 用本地时间差来解决由于js阻塞导致的倒计时延迟的问题 | |
*/ | |
class Timer { | |
/** | |
* 创建一个新的Timer实例 | |
* @param {Object} dates - 包含开始和结束时间的对象,时间可以是日期字符串或者以秒为单位的时间戳 | |
*/ | |
constructor(dates) { | |
// 结束时间(秒) | |
this.finish = this.parseTime(dates.finish) | |
// 开始时间(秒) | |
this.begin = this.parseTime(dates.begin) | |
// 倒计时的总时长(秒) | |
this.total = this.finish - this.begin | |
// 已经过去的时间(秒) | |
this.elapsed = 0 | |
// 倒计时剩余的秒数 | |
this.remain = this.total | |
// JavaScript开始时间(毫秒) | |
this.jsTimestamp = Date.now() | |
// 误差时间(毫秒) | |
this.timeOffset = 0 | |
// 下一次执行的时间(毫秒) | |
this.nextTime = 0 | |
// 计时器ID | |
this.timerId = null | |
} | |
/** | |
* 将输入的时间解析为以秒为单位的时间戳 | |
* @param {string|number} time - 输入的时间,可以是日期字符串或者以秒为单位的时间戳 | |
* @return {number} 以秒为单位的时间戳 | |
*/ | |
parseTime(time) { | |
return typeof time === 'string' ? Math.floor(Date.parse(time.replace(/-/g, '/')) / 1000) : time | |
} | |
/** | |
* 将秒数格式化为DD:HH:MM:SS格式 | |
* @param {number} s - 秒数 | |
* @return {string} 格式化后的时间字符串 | |
*/ | |
format(s) { | |
const DAY_SECONDS = 86400 | |
const HOUR_SECONDS = 3600 | |
const MINUTE_SECONDS = 60 | |
let d = Math.floor(s / DAY_SECONDS) | |
let h = Math.floor((s % DAY_SECONDS) / HOUR_SECONDS) | |
let m = Math.floor((s % HOUR_SECONDS) / MINUTE_SECONDS) | |
s = s % MINUTE_SECONDS | |
d = `${d}`.padStart(2, '0') | |
h = `${h}`.padStart(2, '0') | |
m = `${m}`.padStart(2, '0') | |
s = `${s}`.padStart(2, '0') | |
return `${d}:${h}:${m}:${s}` | |
} | |
/** | |
* 更新倒计时显示,并在需要时停止计时器 | |
* @param {boolean} log - 是否打印日志 | |
*/ | |
update(log = false) { | |
// 如果剩余时间小于0,停止计时器 | |
if (this.remain < 0) return clearTimeout(this.timerId) | |
// 获取当前时间 | |
let now = Date.now() | |
// 计算已经过去的秒数 | |
this.elapsed = Math.ceil((now - this.jsTimestamp) / 1000) | |
// 重新计算开始时间 | |
this.begin = this.finish - this.total + this.elapsed | |
// 重新计算剩余的秒数 | |
this.remain = this.finish - Math.floor(now / 1000) | |
// 每秒更新一次显示 | |
this.timerId = setTimeout(() => this.update(log), 1000) | |
// 计算下一次执行的时间 | |
this.nextTime = 1000 - ((now - this.jsTimestamp) % 1000) | |
// 计算误差 | |
this.timeOffset = 1000 - this.nextTime | |
// 格式化时间 | |
let timeStr = this.format(this.remain) | |
if (log) { | |
console.log(`倒计时:${timeStr}, 剩余时间:${this.remain}s, 耗时:${this.elapsed}s, 误差:${this.timeOffset}ms,下一次执行:${this.nextTime}ms 后`) | |
} | |
} | |
/** | |
* 开始倒计时 | |
* @param {boolean} log - 是否打印日志 | |
*/ | |
start(log = false) { | |
this.update(log) | |
} | |
/** | |
* 停止倒计时 | |
*/ | |
stop() { | |
clearTimeout(this.timerId) | |
} | |
} | |
// 设置开始和结束时间 | |
let dates = { | |
// begin: '2000-01-01 00:00:00', | |
begin: Math.floor(Date.now() / 1000), // 当前时间(秒) | |
finish: '2050-01-01 00:00:00', | |
} | |
// 创建一个新的Timer实例 | |
let timer = new Timer(dates) | |
// 开始倒计时 | |
timer.start(true) | |
// const longTask = () => { | |
// const startTime = Date.now() | |
// const ms = 10 * 1000 | |
// while (Date.now() - startTime < ms) {} | |
// } | |
// longTask() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment