单文件番茄钟专注计时器:纯前端实现的开发记录
为什么做番茄钟
专注工作法里番茄钟是最容易落地的:25 分钟专注 + 5 分钟休息,循环四次后长休息一次。市面上的番茄钟 App 大多要登录、要同步,反而把简单的事情复杂化。我的目标是做一个单文件、零依赖、离线可用的番茄钟网页:打开即用,数据只存在浏览器本地。
功能拆解
核心功能只有四个:
- 计时器:25/5/15 三档时长可切换,开始/暂停/重置
- 任务清单:输入任务名,完成后勾选,清空已完成
- 浏览器通知:倒计时结束时弹出系统通知(即使标签页不在前台)
- 本地持久化:任务清单和累计专注番茄数存 localStorage,刷新不丢
非功能要求:单个 HTML 文件(含内联 CSS/JS),在手机和桌面浏览器都能正常显示。
计时器实现:为什么不用 setInterval 直接减
新手最容易写出的版本是 setInterval(() => seconds--, 1000),但它有两个坑:
- 浏览器标签页切到后台后,
setInterval会被节流,计时变慢; - 系统休眠或卡顿会累积误差。
更稳的做法是记录目标结束时间戳:
let endAt = Date.now() + duration * 1000;
timer = setInterval(() => {
const remain = Math.max(0, endAt - Date.now());
render(remain);
if (remain <= 0) { clearInterval(timer); onFinish(); }
}, 250);
轮询间隔取 250ms 而不是 1000ms,显示更平滑;实际剩余时间永远用时间戳差算,误差只来自单次回调延迟,不会累积。暂停时把当前剩余秒数存下来,恢复时重新算 endAt 即可。
通知权限:先请求再使用
浏览器通知需要用户授权,正确顺序是:页面加载时检查 Notification.permission,如果是 default 就在用户第一次点击"开始"时调用 Notification.requestPermission()——必须在用户手势里请求,否则 Chrome 会直接拒绝。倒计时结束时:
if (Notification.permission === "granted") {
new Notification("番茄钟", { body: "25 分钟专注结束,休息一下吧!" });
}
再加一个页面标题闪烁作为兜底(用户拒绝通知权限时仍有提醒)。
任务清单与 localStorage
任务清单用数组存 {text, done},每次变更后 localStorage.setItem 全量写回。数据量小,全量序列化没有性能问题,反而避免了增量同步的状态错乱。页面加载时读回并渲染。累计完成的番茄数同样存一份,方便看长期趋势。
界面:一个大数字 + 三个按钮
番茄钟的界面越简单越好:屏幕中央一个大号倒计时,下方开始/暂停/重置,顶部三个时长切换 Tab,底部任务清单。配色用深色专注模式(深蓝背景 + 高对比数字),减少视觉干扰。全部用 flexbox 居中布局,媒体查询处理小屏幕。
发布与验证
最终成品是一个独立的 index.html,双击即可在浏览器打开;我还把它部署到了华为云 AI 开发者空间并通过 DevBridge 隧道生成公网预览地址,手机扫码就能用。完整代码随后会开源到 GitCode,敬请期待。
小结
这个项目的技术点都不难,但每个都踩过真实的坑:后台节流、通知权限时机、本地数据同步。把"能用"和"好用"之间的差距补上,靠的正是这些细节。下一个版本打算加白噪音和专注统计曲线,敬请期待。
- 点赞
- 收藏
- 关注作者
评论(0)