diff --git a/README.md b/README.md index 64e3ca4..0e7eb4e 100644 --- a/README.md +++ b/README.md @@ -28,3 +28,8 @@ 一些想做但不确定会不会做的功能 - [ ] 排行榜 - [ ] 联机模式 + +## 补充 +- 图标来自于:[iconify](https://iconify.design/),同时使用了 [icones](https://icones.js.org/) 网站来预览图 +- 有趣的游戏音效来自:[Duolingo](https://www.duolingo.com/),因为我也是个Duolingo软件的忠实用户,很喜欢TA的音效设计。PS:请原谅我未经允许的使用😜 +- 项目技术栈为:Vue3 + Vite5 + TypeScript + TailwindCSS + VueRouter + VueUse + Pinia diff --git a/src/composables/use-countdown.ts b/src/composables/use-countdown.ts index f9c0378..1fadb18 100644 --- a/src/composables/use-countdown.ts +++ b/src/composables/use-countdown.ts @@ -19,14 +19,21 @@ interface Options { /** * 当倒计时结束时 */ - onEnd?: () => void + onFinished?: () => void + + /** + * 当读秒改变时 + * @param second {number} + */ + // onChange?: (second: number) => void } export function useCountdown({ times = 60, interval = 1, immediate = false, - onEnd = () => { }, + onFinished = () => { }, + // onChange = (second: number) => { }, } = {} as Options) { let timeoutId: number @@ -44,7 +51,7 @@ export function useCountdown({ timeoutId = window.setTimeout(() => { remainder.value -= interval - remainder.value ? start(false) : onEnd() + remainder.value ? start(false) : onFinished() }, interval * 1000) } diff --git a/src/composables/use-game-score.ts b/src/composables/use-game-score.ts index f3c9dc5..d0cd987 100644 --- a/src/composables/use-game-score.ts +++ b/src/composables/use-game-score.ts @@ -39,6 +39,7 @@ export function useGameScore( timestamp.value = lastTime lastTimestamp = performance.now() + stopRecording() timestampId = window.setInterval(() => { timestamp.value += 1 }, 1000) @@ -52,23 +53,6 @@ export function useGameScore( showDeltaScore.value = false } - // 当页面不可见时停止计时 - function onVisibilityChange() { - if (document.visibilityState === 'hidden') { - stopRecording() - } else { - startRecording(timestamp.value) - } - } - - document.addEventListener('visibilitychange', onVisibilityChange) - - onBeforeUnmount(() => { - stopRecording() - - document.removeEventListener('visibilitychange', onVisibilityChange) - }) - return { timestamp, gameScore, diff --git a/src/views/game/[level].vue b/src/views/game/[level].vue index 62e9179..21040e3 100644 --- a/src/views/game/[level].vue +++ b/src/views/game/[level].vue @@ -67,16 +67,24 @@ const { const { value: countdown, - start: startCountdown, - reset: resetCountdown, -} = useCountdown({ times: levelConfig.internal }) + start: startPreviewCountdown, + reset: resetPrewiewCountdown, + pause: pausePreviewCountdown, +} = useCountdown({ + times: levelConfig.internal, + // 当倒计时结束时开始游戏并 + onFinished: onFinishedPreviewCountdown, +}) -let startTimeoutId = -1 +// 当预览倒计时结束时开始游戏计分, 取消所有预览块, 并设置游戏状态 +function onFinishedPreviewCountdown() { + // 开始计分计时 + startRecording() + uncheckAllBlocks() + setGameStatus('playing') +} async function startGame() { - resetCountdown() - startCountdown() - updateHighestScoreStatus() generateRandomTargetBlock() @@ -89,14 +97,9 @@ async function startGame() { // 设置游戏状态为预览模式 setGameStatus('previewing') - clearTimeout(startTimeoutId) - // 延迟关闭预览模式 - startTimeoutId = window.setTimeout(() => { - // 开始计分计时 - startRecording() - uncheckAllBlocks() - setGameStatus('playing') - }, levelConfig.internal * 1000) + // 重置读秒并重新开始倒计时 + resetPrewiewCountdown() + startPreviewCountdown() } function onCheckResult() { @@ -186,12 +189,28 @@ function formatScore(score: number) { return numStr.replace(reg, ',') } +// 当浏览器游戏标签不可见时暂停计时 +function onGameTabVisibilityChange() { + if (document.visibilityState === 'hidden') { + stopRecording() + pausePreviewCountdown() + return + } + + // 可见时再重新开始读秒和预览模式的计时 + startRecording(timestamp.value) + startPreviewCountdown() +} + onMounted(() => { startGame() + + document.addEventListener('visibilitychange', onGameTabVisibilityChange) }) onBeforeUnmount(() => { - clearTimeout(startTimeoutId) + stopRecording() + document.removeEventListener('visibilitychange', onGameTabVisibilityChange) }) @@ -204,7 +223,7 @@ onBeforeUnmount(() => { -