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

JavaScript秒表页面刷新后保留计时状态续跑实现方案

JavaScript秒表刷新续跑+数据库时间读取实现方案

原实现采用每秒累加秒数的逻辑,存在刷新丢状态、后台节流计时不准的问题,我们通过时间戳差值计算+状态持久化的方案实现需求,完整实现如下:


核心改动点

  • 替换原每秒累加计数逻辑,用「累计基准时长+当前时间戳差值」计算总时长,从根源解决计时不准、断点无法续算的问题
  • 用localStorage持久化存储累计时长、运行状态,页面刷新后自动恢复
  • 预留数据库读取适配层,初始化时优先拉取数据库存储的时间点,支持跨端续跑
  • 完全保留原有Start/Pause的交互逻辑,不改变原有操作习惯

完整代码

HTML部分(原有结构无需修改)

<button id="start">Start</button>
<button id="pause">Pause</button>
<div id="output"></div>

JavaScript部分(替换原有JS逻辑即可)

const showTime = document.querySelector("#output");
const startTimeButton = document.querySelector("#start");
const pauseTimeButton = document.querySelector("#pause");

const STORAGE_KEY = "stopwatch_status";
let elapsedMs = 0; // 已累计的毫秒数
let startTimestamp = null; // 本次启动的起始时间戳
let interval = null;
let isRunning = false;

// 时间格式化工具
const formatTime = (totalMs) => {
  const totalSeconds = Math.floor(totalMs / 1000);
  const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
  const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
  const secs = String(totalSeconds % 60).padStart(2, '0');
  return `${hours}:${minutes}:${secs}`;
};

// 更新计时显示
const updateDisplay = () => {
  const currentTotal = isRunning 
    ? elapsedMs + (Date.now() - startTimestamp)
    : elapsedMs;
  showTime.textContent = formatTime(currentTotal);
};

// 保存状态到本地存储,供刷新后恢复
const saveStatusToLocal = () => {
  const saveData = {
    elapsedMs: isRunning ? elapsedMs + (Date.now() - startTimestamp) : elapsedMs,
    isRunning
  };
  localStorage.setItem(STORAGE_KEY, JSON.stringify(saveData));
};

// 从数据库拉取已存储的计时状态,替换成实际接口逻辑即可
const fetchDBStatus = async () => {
  // 示例:替换为实际接口请求
  // const resp = await fetch('/api/stopwatch/status');
  // return await resp.json(); // 返回格式要求:{ elapsedMs: number, isRunning: boolean }
  return null; // 无数据库数据时返回null,自动走本地存储逻辑
};

// 启动计时
const startTimer = () => {
  if (isRunning) return;
  isRunning = true;
  startTimestamp = Date.now();
  // 切换按钮状态
  pauseTimeButton.style.display = "block";
  startTimeButton.style.display = "none";
  // 每100ms刷新一次显示,流畅度更高
  interval = setInterval(() => {
    updateDisplay();
    saveStatusToLocal();
  }, 100);
  updateDisplay();
  saveStatusToLocal();
};

// 暂停计时
const pauseTimer = () => {
  if (!isRunning) return;
  isRunning = false;
  // 结算本次运行的时长到累计值
  elapsedMs += Date.now() - startTimestamp;
  startTimestamp = null;
  clearInterval(interval);
  interval = null;
  // 切换按钮状态
  pauseTimeButton.style.display = "none";
  startTimeButton.style.display = "block";
  updateDisplay();
  saveStatusToLocal();
};

// 页面初始化,恢复状态
const init = async () => {
  // 优先读取数据库存储的状态
  let initStatus = await fetchDBStatus();
  // 数据库无数据则读取本地存储
  if (!initStatus) {
    const localData = localStorage.getItem(STORAGE_KEY);
    initStatus = localData ? JSON.parse(localData) : null;
  }

  // 恢复已存储的状态
  if (initStatus) {
    elapsedMs = initStatus.elapsedMs || 0;
    isRunning = initStatus.isRunning || false;
    updateDisplay();
    // 若刷新/关闭前是运行状态,自动续跑
    if (isRunning) {
      startTimestamp = Date.now();
      pauseTimeButton.style.display = "block";
      startTimeButton.style.display = "none";
      interval = setInterval(() => {
        updateDisplay();
        saveStatusToLocal();
      }, 100);
    }
  } else {
    showTime.textContent = "00:00:00";
  }

  // 绑定按钮事件
  startTimeButton.addEventListener("click", startTimer);
  pauseTimeButton.addEventListener("click", pauseTimer);
};

init();

使用说明

  • 刷新续跑:运行状态下刷新页面,会自动读取上次的计时断点,无需手动点击Start就能继续计时,暂停状态下刷新会保留暂停时的时间值
  • 数据库适配:如果需要对接后端存储,只需要修改fetchDBStatus函数里的请求逻辑,接口返回累计毫秒数elapsedMs和运行状态isRunning两个字段即可,初始化时会优先使用数据库返回的时间点
  • 精度保障:基于时间戳差值计算时长,就算浏览器后台节流导致setInterval触发延迟,最终计时结果也不会出现偏差

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 05:55:05