Skip to content

Instantly share code, notes, and snippets.

@nekomeowww
Last active January 4, 2022 08:28
Show Gist options
  • Save nekomeowww/0d1fa7f6d2b688c373662a5c50d9c255 to your computer and use it in GitHub Desktop.
Save nekomeowww/0d1fa7f6d2b688c373662a5c50d9c255 to your computer and use it in GitHub Desktop.
支持管理和同步系统时间的计时器实现
window.globalScope = {
syncedTimer: {
/**
* 计时器对象,保存了所有计时器的 id 和运行、重设状态
*
* @example { main : { "1": { running: true, reset: false }, "2": { running: false, reset: true } } }
*/
mTimers: {
main: {}
},
/**
* 计时器自增计数对象,保存了所有计时器的自增计数
*
* @example { main: 1, tag2: 1 }
*/
timerCount: {
main: 0
},
/**
* 创建计时器
* @param {String} tag 标签 - 用于计时器的管理和隔离
* @param {Function} createFunc - 创建函数,用于构建计时器的函数,该函数会立即被调用
*/
createTimer: function (tag = 'main', createFunc = function (timerFunc) { }) {
const timerObj = this
timerObj.mTimers[tag] = {}
function clearResetTimer() {
try {
const mTimerKeys = Object.keys(timerObj.mTimers[tag])
mTimerKeys.forEach(key => {
if (timerObj.mTimers[tag][key].reset) {
timerObj.mTimers[tag][key].running = false
setTimeout(() => delete timerObj.mTimers[tag][key], 2000)
}
})
} catch (err) {
console.error(err)
}
}
/**
* timer 计时器
* @param callback - 回调函数,此处和 createFunc 不同,是每秒执行的函数
*/
function timer(callback = function (timerNow) {}) {
try {
if (!timerObj.timerCount[tag]) timerObj.timerCount[tag] = 0
timerObj.timerCount[tag]++
const key = String(timerObj.timerCount[tag])
// 初始化和创建计时器
if (!timerObj.mTimers[tag][key]) {
function pauseTimer() {
console.log('pausing timer', tag, key)
timerObj.mTimers[tag][key].paused = true
}
function resumeTimer() {
console.log('resuming timer', tag, key)
timerObj.mTimers[tag][key].paused = false
}
timerObj.mTimers[tag][key] = { running: true, paused: false, pauseTimer, resumeTimer }
// 清空老旧计时器
clearResetTimer()
}
// 如果计时器 ID 超过了 10000,则重置为 0
if (timerObj.timerCount[tag] > 10000) timerObj.timerCount[tag] = 0
// 支持校准计时器偏差值得计时器函数
const innerTimerFunction = function () {
// 自我重设
if (timerObj.mTimers[tag][key] && timerObj.mTimers[tag][key].reset) {
timerObj.mTimers[tag][key].reset = false
timerObj.mTimers[tag][key].running = false // 复位
return
}
// 没有在运行了
if (timerObj.mTimers[tag][key] && !timerObj.mTimers[tag][key].running) return
const timerNow = Date.now() + 10 // 手动偏移
let runNext = 0
if (timerObj.mTimers[tag][key] && timerObj.mTimers[tag][key].paused) setTimeout(innerTimerFunction, timerNow + 1000 - Date.now())
else if (timerObj.mTimers[tag][key] && !timerObj.mTimers[tag][key].paused) runNext = callback(timer)
// 为了保证计时器的精度,此处进行校准
if (runNext) setTimeout(innerTimerFunction, timerNow + 1000 - Date.now())
}
// 默认执行一次
innerTimerFunction()
} catch (err) {
console.error(err)
}
}
// 直接执行并传递计时器函数,可以在创建函数中自定义计时器的运行方式
createFunc(timer)
},
/**
* 清空先前在运行的计时器
* @param tag 标签 - 用于计时器的管理和隔离
*/
clearPreviousRunningTimers: function (tag = 'main') {
const timerObj = this
if (!timerObj.mTimers[tag]) return
let runningTimers = []
Object.keys(timerObj.mTimers[tag]).forEach((key) => {
if (timerObj.mTimers[tag][key].running) runningTimers.push(key)
else delete timerObj.mTimers[tag][key]
})
// 按照 ID 排序
runningTimers = runningTimers.sort((a, b) => parseInt(a) - parseInt(b))
// 如果有存在的计时器,则关闭
if (runningTimers.length > 0) runningTimers.forEach((key) => timerObj.mTimers[tag][key].reset = true)
},
pauseTimers(tag = 'main') {
const timerObj = this
if (tag !== '*' && !timerObj.mTimers[tag]) return
let nonPausingTimers = []
if (tag === '*') {
Object.keys(timerObj.mTimers).forEach((tagKey) => {
console.log('tag', tagKey)
Object.keys(timerObj.mTimers[tagKey]).forEach((key) => {
console.log('tag', tagKey, 'key', key)
if (timerObj.mTimers[tagKey][key] && !timerObj.mTimers[tagKey][key].paused) nonPausingTimers.push({tag: tagKey, key})
})
})
} else {
Object.keys(timerObj.mTimers[tag]).forEach((key) => {
if (!timerObj.mTimers[tag][key].paused) nonPausingTimers.push({tag, key})
})
}
if (nonPausingTimers.length > 0) nonPausingTimers.forEach((obj) => timerObj.mTimers[obj.tag][obj.key].pauseTimer())
},
resumeTimers(tag = 'main') {
const timerObj = this
if (tag !== '*' && !timerObj.mTimers[tag]) return
let pausingTimers = []
if (tag === '*') {
Object.keys(timerObj.mTimers).forEach((tagKey) => {
Object.keys(timerObj.mTimers[tagKey]).forEach((key) => {
if (timerObj.mTimers[tagKey][key] && timerObj.mTimers[tagKey][key].paused) pausingTimers.push({tag: tagKey, key})
})
})
} else {
Object.keys(timerObj.mTimers[tag]).forEach((key) => {
if (timerObj.mTimers[tag][key].paused) pausingTimers.push({tag, key})
})
}
if (pausingTimers.length > 0) pausingTimers.forEach((obj) => timerObj.mTimers[obj.tag][obj.key].resumeTimer())
}
}
}
const HHMMSS = (milli) => {
const s = Math.floor(parseInt(milli, 10) / 1000)
const hours = Math.floor(s / 3600)
const minutes = Math.floor(s / 60) % 60
const seconds = s % 60
return [hours,minutes,seconds]
.map(v => v < 10 ? "0" + v : v)
.filter((v,i) => v !== "00" || i > 0)
.join(":")
}
const newCountDown1Hour = (startTimestamp) => {
globalScope.syncedTimer.createTimer('countDown1Hour', function (timerFunc) {
try {
globalScope.syncedTimer.clearPreviousRunningTimers('countDown1Hour')
let ts = Date.now()
if (startTimestamp) ts = startTimestamp
const now = new Date(ts)
let countDownEnd = new Date(ts)
countDownEnd.setHours(now.getHours() + 1, now.getMinutes(), now.getSeconds(), 0)
let ellapsedTime = 0
const countDown = function (timerNow) {
let countDownSeconds = (countDownEnd.getTime() - now.getTime()) - ellapsedTime
if (countDownSeconds <= 0) {
countDownSeconds = 0
ellapsedTime = 0
}
document.querySelector('#countDown1h').innerHTML = HHMMSS(countDownSeconds)
ellapsedTime += 1000 // 模拟时钟
return countDownSeconds > 0
}
timerFunc(countDown)
} catch (err) {
console.error(err)
}
})
}
newCountDown1Hour(Date.now())
> newCountDown1Hour(Date.now())
59:59
< undefined
59:58
59:57
59:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment