功能概述
番茄钟是一个基于番茄工作法的专注计时器,完全在浏览器端运行,无需后端支持。核心特性包括:
- 三种计时模式:专注(25min)、短休息(5min)、长休息(15min)
- 圆形 SVG 进度条动画,不同模式不同颜色
- 白噪音合成:6种环境音(雨声、森林、海浪等)
- 任务清单:添加/完成/删除任务
- 今日统计:完成番茄数、专注分钟、连续番茄、目标进度
- 完成提醒音:Web Audio API 合成提示音
- 数据持久化:localStorage 保存今日统计
页面架构
番茄钟作为独立页面类型(type: pomodoro)集成到 Hugo 主题中,与出行轨迹、在线工具等页面并列在导航栏「探索」菜单下。
1content/pomodoro/_index.md ← 内容页面(Front Matter)
2layouts/pomodoro/single.html ← 页面模板(HTML + CSS + JS)
内容页面
1---
2title: "番茄钟"
3type: pomodoro
4url: /pomodoro/
5---
首页排除
在 layouts/index.html 中排除 pomodoro 类型,避免在首页文章卡片中显示:
1{{ $allArticles := where ... "Type" "not in" (slice "moments" "amap" "pomodoro") }}
核心实现
1. 圆形进度条
使用 SVG stroke-dasharray 和 stroke-dashoffset 实现环形进度条:
1<svg viewBox="0 0 280 280">
2 <circle class="pomo-clock-bg" cx="140" cy="140" r="126"></circle>
3 <circle class="pomo-clock-progress" cx="140" cy="140" r="126"
4 stroke-dasharray="791.68" stroke-dashoffset="0"></circle>
5</svg>
进度计算:
1var CIRCUMFERENCE = 2 * Math.PI * 126; // ≈ 791.68
2var progress = 1 - (state.timeLeft / state.totalTime);
3var offset = CIRCUMFERENCE * (1 - progress);
4element.style.strokeDashoffset = offset;
不同模式使用不同颜色:
- 专注:红色渐变
#ef4444 → #f97316 - 短休息:绿色渐变
#22c55e → #10b981 - 长休息:紫色渐变
#6366f1 → #8b5cf6
2. 计时器逻辑
使用 setInterval 每秒更新,状态机管理三种模式切换:
1var state = {
2 mode: 'work', // 当前模式
3 running: false, // 是否运行中
4 timeLeft: 25 * 60, // 剩余秒数
5 totalTime: 25 * 60, // 总秒数
6 pomosCompleted: 0, // 已完成番茄数
7 streak: 0, // 连续番茄
8 totalMinutes: 0 // 总专注分钟
9};
10
11function tick() {
12 if (state.timeLeft <= 0) { complete(); return; }
13 state.timeLeft--;
14 updateDisplay();
15}
完成一个番茄后自动切换:
- 每 4 个番茄后进入长休息
- 其他情况进入短休息
- 休息结束后自动切回专注模式
3. 白噪音合成
使用 Web Audio API 生成白噪音,无需加载外部音频文件:
1function createNoise(type) {
2 var audioCtx = new AudioContext();
3 var bufferSize = 2 * audioCtx.sampleRate;
4 var buffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate);
5 var data = buffer.getChannelData(0);
6
7 // 不同类型使用不同的噪声算法
8 if (type === 'rain') {
9 for (var i = 0; i < bufferSize; i++)
10 data[i] = (Math.random() * 2 - 1) * 0.3;
11 } else if (type === 'ocean') {
12 for (var i = 0; i < bufferSize; i++) {
13 var wave = Math.sin(i / audioCtx.sampleRate * Math.PI * 0.1);
14 data[i] = (Math.random() * 2 - 1) * 0.2 * (0.5 + 0.5 * wave);
15 }
16 }
17 // ... 其他类型
18
19 var source = audioCtx.createBufferSource();
20 source.buffer = buffer;
21 source.loop = true;
22 source.connect(gainNode);
23 gainNode.connect(audioCtx.destination);
24}
6种白噪音的区别:
| 类型 | 音量 | 特点 |
|---|---|---|
| 雨声 | 0.3 | 纯随机噪声 |
| 森林 | 0.15 | 低音量噪声 |
| 海浪 | 0.2 | 正弦波调制噪声 |
| 篝火 | 0.1 | 低音量噪声 |
| 咖啡厅 | 0.08 | 极低音量噪声 |
| 微风 | 0.12 | 中等音量噪声 |
同一时间只能播放一种白噪音,切换时自动关闭前一个。
4. 提示音
计时完成时使用 Web Audio API 合成双音提示:
1function playNotification() {
2 var ctx = new AudioContext();
3 // 第一声 800Hz
4 var osc = ctx.createOscillator();
5 osc.frequency.value = 800;
6 osc.type = 'sine';
7 // 0.3秒后第二声 1000Hz
8 setTimeout(function() {
9 var osc2 = ctx.createOscillator();
10 osc2.frequency.value = 1000;
11 }, 300);
12}
5. 数据持久化
今日统计数据保存到 localStorage,按日期重置:
1var today = new Date().toDateString();
2var saved = JSON.parse(localStorage.getItem('pomo_stats'));
3
4if (saved && saved.date === today) {
5 // 恢复今日数据
6 state.pomosCompleted = saved.pomos;
7 state.totalMinutes = saved.minutes;
8 state.streak = saved.streak;
9}
6. 页面标题
运行时在浏览器标签页显示倒计时:
1document.title = (state.running
2 ? String(min).padStart(2,'0') + ':' + String(sec).padStart(2,'0') + ' - '
3 : '') + '番茄钟';
样式统一
番茄钟页面的布局和样式与出行轨迹、在线工具等页面保持统一:
- Banner:180px 高度渐变背景,圆角 20px,阴影效果
- 布局:隐藏侧边栏,内容区最大宽度 860px 居中
- 暗色模式:完整适配,渐变背景切换为深色调
- 响应式:900px 和 600px 两个断点,移动端适配
- 图标动画:标题图标带脉冲动画,与出行轨迹的弹跳动画风格一致
技术要点总结
| 模块 | 技术方案 |
|---|---|
| 进度条 | SVG stroke-dashoffset 动画 |
| 计时器 | setInterval + 状态机 |
| 白噪音 | Web Audio API AudioBuffer 合成 |
| 提示音 | Web Audio API OscillatorNode |
| 数据持久化 | localStorage 按日期存储 |
| 页面标题 | document.title 动态更新 |
| 任务清单 | 纯 JS 数组管理 + DOM 渲染 |
| 样式 | CSS 变量 + 暗色模式 + 响应式 |
留言评论
期待你的想法评论加载中