浏览器倒计时如何避免累计误差:截止时间、暂停与恢复
做一个浏览器倒计时,最容易想到的实现是每隔一秒把剩余秒数减一。但浏览器的定时回调并不保证精确到点执行:主线程忙碌、页面进入后台或设备休眠,都可能让回调延后。这里结合“秒刻”专注倒计时的实际实现,说明如何把时间计算和界面刷新分开。
一、让截止时间决定剩余量
假设用户选择二十五分钟,开始时记录截止时间 deadline = Date.now() + 25 * 60 * 1000。后续每次刷新,重新计算 remaining = Math.max(0, deadline - Date.now()),而不是按回调次数累计减秒。
const totalMs = 25 * 60 * 1000;
const deadline = Date.now() + totalMs;
function remainingMs() {
return Math.max(0, deadline - Date.now());
}
function displaySeconds() {
return Math.ceil(remainingMs() / 1000);
}
这样,即使某次刷新晚了几秒,下次仍会按截止时间修正显示,不会把漏掉的回调一直累积为误差。setInterval 只负责提醒页面重绘,它的调用次数不作为经过时间的依据。采用 Math.ceil 可以让剩余不足一秒时仍显示一秒,真正归零后再进入结束状态。
二、暂停和继续需要保存不同的数据
运行中保存截止时间;暂停时,先用当前时间更新剩余毫秒数,再把状态改为暂停。暂停期间不继续扣减剩余量。用户点击继续时,以当前时间加上保存的剩余量,建立新的截止时间。
// 暂停
left = Math.max(0, deadline - Date.now());
state = 'paused';
// 继续
deadline = Date.now() + left;
state = 'running';
如果暂停后继续沿用原来的截止时间,暂停期间也会被算入倒计时。因此,暂停不能只停止更新文字,还必须改变时间计算的依据。对于恰好已经归零的情况,应先转为结束状态,再按“重新开始”的语义处理按钮。
三、用四种状态约束按钮行为
秒刻将计时分为准备、运行、暂停和结束。主按钮分别对应开始、暂停、继续和再来一轮;重置则回到当前设定的完整时长。选择新的预设或应用自定义时长,也会重置当前时段,避免界面上的分钟数和实际运行时长不一致。
自定义输入在页面层使用整数范围校验,计时核心再次检查是否为一到一百八十之间的整数。两层校验职责不同:输入框帮助用户及时发现错误,核心校验避免其他调用路径传入非法值。
四、验证时间逻辑时,不必真的等二十五分钟
计时核心接收一个“获取当前时间”的函数,默认使用 Date.now。验证时传入可手动推进的时钟,检查运行后的剩余量、暂停期间是否保持不变、继续后能否归零,以及结束后重新开始和重置的行为。实际页面另行检查预设、自定义、暂停按钮与标签页标题是否同步。
本次验证覆盖这些状态转换和输入边界,浏览器交互检查也已通过。页面恢复可见时会主动刷新一次,但这并不意味着后台能准时发出提醒。系统时钟调整会影响基于 Date.now 的计算,刷新页面也会清空当前状态;需要持久计时、跨设备同步或可靠通知时,应另行设计存储和时间来源。
这种实现适用于轻量学习和专注工具:时间逻辑保持独立,界面刷新可以按需要调整频率,暂停和结束规则也更容易核对。
- 点赞
- 收藏
- 关注作者
评论(0)