Last active
January 4, 2022 08:28
-
-
Save nekomeowww/0d1fa7f6d2b688c373662a5c50d9c255 to your computer and use it in GitHub Desktop.
支持管理和同步系统时间的计时器实现
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
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()) | |
} | |
} | |
} |
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
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()) |
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
> 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