You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

关键修改说明

  1. 累计时长管理:新增totalElapsed变量存储暂停前的已用时长,恢复计时时将当前周期的时间差与累计时长相加,解决恢复计时跳回初始值的问题
  2. 无交互防抖暂停:通过debounceTimer实现,每次调用startChrono()(交互触发)时重置定时器,超时后自动执行pauseChrono(),可通过INACTIVE_TIMEOUT调整超时时间
  3. 定时器统一管理:将intervalId设为响应式变量,创建前检查是否已有运行的定时器,避免重复创建导致计时加速;暂停和重置时主动清除定时器
  4. 按钮文本优化:根据endTime状态动态切换按钮文本,提升用户体验
  5. 格式化逻辑优化:为秒数添加padStart(2, '0'),确保显示两位数字,格式更规范

内容的提问来源于stack exchange,提问作者EdouardHrgt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 21:25:08