浏览器倒计时怎样避免越走越慢:从结束时间到刷新恢复
做一个25分钟专注计时器,最容易写出的版本是每秒把数字减一。前台页面里看起来正常,切换标签页、暂停再继续、刷新页面之后,问题才逐渐暴露。
本文用新做的“节奏·专注时钟”说明另一种实现:保存结束时间,定时器只负责更新画面。这个单文件页面支持25分钟专注、5分钟短休息和15分钟长休息,无需后端或第三方依赖。
一、回调次数不等于经过时间
setInterval 表示安排回调,不保证每次都准时执行。页面切到后台、主线程忙于其他任务时,渲染更新可能变慢。如果每次回调只减一秒,显示时间就会落后于实际经过时间。
这次实现不累计回调次数,而是在开始时记录结束时间:
state.deadline = Date.now() + state.remaining;
state.running = true;
每次绘制重新计算剩余毫秒数:
const left = () => state.running
? Math.max(0, state.deadline - Date.now())
: state.remaining;
即使两次绘制间隔突然变长,下次回调仍然依据当前时间计算结果。实现中每250毫秒刷新一次画面,显示秒数向上取整,避免还剩不足一秒时提前显示零。
这种方案依赖系统时钟。用户手动调整时间或系统发生校时,会影响倒计时结果。它适合日常专注工具,不能直接当作计费或考试的可信计时依据。
二、暂停保存时长,继续重建结束时间
运行状态保存 deadline;暂停状态保存 remaining。暂停先读取当前剩余时长,再清除结束时间:
state.remaining = left();
state.running = false;
state.deadline = null;
继续时用“当前时间加剩余时长”建立新的结束时间。这样暂停期间不会消耗本轮时间。
切换模式则重置全部计时状态,避免上一种模式的结束时间继续作用于下一种模式。重置按钮同样停止计时,恢复当前模式的完整时长。
三、刷新恢复必须验证存储数据
页面将模式、剩余时间、运行状态、结束时间和完成标记保存到 localStorage。初始化时先检查模式是否存在、数字是否有限且处于合理范围、状态字段是否为布尔值,再采用恢复数据。
运行中刷新,保留原来的结束时间;暂停后刷新,保留暂停的剩余时长。重新打开时如果结束时间已经过去,显示“本轮已完成”,而不是把负数显示出来或自动开始下一轮。
本地存储可能不可用,读取和写入都包在 try/catch 中。此时计时器仍可在当前页面工作,只是不再承诺刷新恢复。页面也明确说明数据保存在当前浏览器,关闭网页后不会发出提醒。
四、用可控时间核验边界
本次使用浏览器自动化的时钟控制核验了以下行为,而不是实际等待25分钟:
| 场景 | 观察结果 |
|---|---|
| 初始状态 | 25:00 |
| 开始后推进61秒 | 23:59 |
| 暂停后再推进90秒 | 仍为23:59 |
| 暂停状态刷新 | 保留23:59并显示已暂停 |
| 继续并推进剩余时间 | 00:00,本轮已完成 |
| 切换短休息 | 05:00 |
| 休息运行30秒后刷新 | 保留04:30 |
| 点击重新计时 | 恢复05:00并停止运行 |
页面还为开始、暂停、完成状态提供文字反馈。空格键可开始或暂停;按钮获得焦点时保留浏览器原生键盘行为,避免一次按键触发两次切换。
五、部署时再核对一次正文
单文件静态页面可以直接用Nginx托管。将文件放到专用目录,配置监听端口后先执行 nginx -t,再重新加载服务,随后检查访问响应。
这次部署除了确认HTTP 200,还对比了公网返回正文与本地HTML的SHA-256,结果一致。这个检查能发现“端口通了,却还在展示默认欢迎页”等问题。配置重新加载后应再次请求页面,避免把重新加载前的一次响应误当作最终结果。
一个小计时器不需要复杂框架,但需要明确区分时间来源、运行状态、保存内容和恢复规则。把这些边界写清楚,暂停、后台切换和刷新就能采用同一套规则处理。
- 点赞
- 收藏
- 关注作者
评论(0)