Vue.js实现带自动暂停功能的计时器问题求助
解决Vue记忆游戏计时器的启停与防抖问题
需求
- 点击
startChrono()触发交互后,在HTML中显示格式化后的计时时长 - 一段时间无交互或游戏结束时自动停止计时
- 再次交互时自动恢复计时
- 可完全重置代码及DOM中的计时数据
修改后的完整代码
<script setup> import { ref } from 'vue'; const startTime = ref(0); // 当前计时的起始时间戳 const endTime = ref(false); // 是否暂停计时 const elapsedTime = ref(0); // 当前显示的总时长(秒) const totalElapsed = ref(0); // 累计已用时长(暂停时保存) const isGameActive = ref(false); const intervalId = ref(null); // 定时器ID,用于统一管理 const debounceTimer = ref(null); // 防抖定时器,用于无交互自动暂停 const INACTIVE_TIMEOUT = 5000; // 无交互自动暂停的超时时间(毫秒) function startChrono() { // 重置防抖定时器 clearTimeout(debounceTimer.value); debounceTimer.value = setTimeout(() => { pauseChrono(); }, INACTIVE_TIMEOUT); isGameActive.value = true; // 如果是暂停后恢复,基于累计时长继续计时 if (endTime.value) { startTime.value = new Date().getTime(); endTime.value = false; } else { // 首次开始,重置所有计时数据 totalElapsed.value = 0; startTime.value = new Date().getTime(); endTime.value = false; } // 确保只有一个定时器运行 if (!intervalId.value) { updateChrono(); } } function pauseChrono() { if (endTime.value) { // 从暂停恢复 startTime.value = new Date().getTime(); endTime.value = false; // 重置防抖定时器 clearTimeout(debounceTimer.value); debounceTimer.value = setTimeout(() => { pauseChrono(); }, INACTIVE_TIMEOUT); } else { // 暂停计时,保存累计时长 const currentTime = new Date().getTime(); totalElapsed.value += Math.floor((currentTime - startTime.value) / 1000); endTime.value = true; isGameActive.value = false; } // 暂停时清除定时器 if (intervalId.value) { clearInterval(intervalId.value); intervalId.value = null; } } function updateChrono() { intervalId.value = setInterval(() => { if (!endTime.value) { const currentTime = new Date().getTime(); // 总时长 = 累计时长 + 当前周期的已用时间 elapsedTime.value = totalElapsed.value + Math.floor((currentTime - startTime.value) / 1000); } }, 1000); } function formatTime(seconds) { if (seconds < 0 || seconds > 3600) { return '0 s'; } const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; return minutes === 0 ? `${remainingSeconds} s` : `${minutes} min ${remainingSeconds.toString().padStart(2, '0')} s`; } function resetChrono() { // 清除所有定时器 clearInterval(intervalId.value); clearTimeout(debounceTimer.value); intervalId.value = null; debounceTimer.value = null; // 重置所有状态 isGameActive.value = false; startTime.value = 0; endTime.value = true; elapsedTime.value = 0; totalElapsed.value = 0; } </script> <template> <main> <h1> Time:<span>{{ formatTime(elapsedTime) }}</span> </h1> <button @click="startChrono()">{{ endTime ? 'Resume' : 'Start' }}</button> <button @click="pauseChrono()">{{ endTime ? 'Start' : 'Pause' }}</button> <button @click="resetChrono()">Reset</button> </main> </template> <style scoped> main { background-color: lightslategray; padding: 5rem; } button { font-weight: bold; font-size: 18px; padding: 0.2rem 1rem; margin-right: 1rem; margin-block: 1rem 0; cursor: pointer; } span { color: rgb(2, 255, 120); margin-left: 1rem; } </style>
关键修改说明
- 累计时长管理:新增
totalElapsed变量存储暂停前的已用时长,恢复计时时将当前周期的时间差与累计时长相加,解决恢复计时跳回初始值的问题 - 无交互防抖暂停:通过
debounceTimer实现,每次调用startChrono()(交互触发)时重置定时器,超时后自动执行pauseChrono(),可通过INACTIVE_TIMEOUT调整超时时间 - 定时器统一管理:将
intervalId设为响应式变量,创建前检查是否已有运行的定时器,避免重复创建导致计时加速;暂停和重置时主动清除定时器 - 按钮文本优化:根据
endTime状态动态切换按钮文本,提升用户体验 - 格式化逻辑优化:为秒数添加
padStart(2, '0'),确保显示两位数字,格式更规范
内容的提问来源于stack exchange,提问作者EdouardHrgt
相关产品推荐
相关产品推荐

