Skip to content

Instantly share code, notes, and snippets.

@zzjtnb
Last active February 5, 2024 11:01
Show Gist options
  • Save zzjtnb/79160e5dfc0b42e102c21aaa3e08d3d4 to your computer and use it in GitHub Desktop.
Save zzjtnb/79160e5dfc0b42e102c21aaa3e08d3d4 to your computer and use it in GitHub Desktop.
用本地时间差来解决由于js阻塞导致的倒计时延迟的问题
/**
* 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