使用方法
- 页面打开时表格里已经有三组示例:07:00 起床(每天、贪睡 5 分钟)、13:30 午休(工作日、贪睡 10 分钟)、15:00 喝水(每天、不贪睡),三组音量都是 60。可以直接把它们改成你要的时间,也可以全部删掉重建。
- 新增一组:先在「小时」填 0~23 的整数、「分钟」填 0~59 的整数,再写一个不超过 20 字的标签(例如「站起来活动」),然后选重复规则、贪睡时长和音量,点「添加提醒」。在小时或分钟输入框里直接按 Enter 也能添加。
- 「重复规则」有四档:仅一次只响一次,响完自动停用;每天每天都响;工作日只在周一至周五响;周末只在周六、周日响。判定依据是浏览器给出的本机星期(0 表示周日)。
- 表格里的「下次响铃」直接告诉你这组提醒下一次会在什么时候触发,用「今天 / 明天 / 周几 + 时刻」表示;倒计时数字每秒刷新一次,可以核对是不是和你想的一致。
- 到点后页面上方会出现红色提示条并循环蜂鸣。点停止响铃结束;点贪睡会按这组提醒设定的贪睡时长,在「当前时刻 + N 分钟」处临时插入一组新提醒,原来那一组的时间点不受影响。
- 「试听蜂鸣」只响一声,用来确认设备音量和浏览器音频是否正常,不会写入任何提醒记录;「导出 CSV」把整张表存成带 BOM 的 .csv;「复制对照表」把当前清单以 TSV 复制到剪贴板。
这个闹钟能解决什么问题
一是电脑前的时间锚点。长时间盯屏幕的人最需要的不是「几点起床」,而是「什么时候该停一下」——写代码、写方案、开长会的时候,抬头发现在椅子上坐了三个小时是常态。把几组提醒常驻在这个页面上,比在手机上单独设一个提醒更顺手,因为工作时注意力本来就在电脑上。
二是设备自带的定时功能不好表达的多组规则。手机自带的提醒通常一次只能选「每天」或某几天,而这里的重复规则是明确定义的四种,工作日与周末分开处理,不用手动去勾五个开关;一套规则配好就能长期挂在那,不用每天重设。
三是不需要安装、不需要任何权限。本页不申请通知权限、不读通讯录、不常驻后台进程,它是一个网页,关掉就彻底结束,不会在系统托盘里留东西。
四是配合会议、课堂、实验做限时提示。测试环境里定时提醒、直播前的准备时间、动手实验的计时节点,都可以用「仅一次」加一个长标签表达,到点蜂鸣提醒,不用盯着时钟。
本页不做这些事:不推送系统级通知、不在页面关闭后继续跑、不跨设备同步、不解析自然语言(「明早七点」这类描述请自己换算成 07:00)。所有数据只写在本机浏览器的 localStorage 里。
报时原理:轮询比对、半开区间与 AudioContext 蜂鸣
核心是一个每秒触发一次的 setInterval,但它不靠 tick 次数报时。很多用 setInterval 写的闹钟会把「触发了多少次」累加起来当时间用,这是错的:定时器只能保证「不早于」指定间隔,实际每次都会晚一点点。实测统计里常见 1~5 毫秒的抖动,按每次晚 5 毫秒算,3600 次(1 小时)就累计到 18 秒,24 小时能差 7.2 分钟。本页每一跳都直接读 new Date(),拿到的永远是本机真实时间,不累加任何计数。
判定用的是半开区间 (上次检查时刻, 本次检查时刻]。每一跳把「上一跳的时间戳」到「这一跳的时间戳」当成一个窗口,再看每组提醒在这个窗口里是否正好有一个触发点:命中就响,没命中就等下一跳。这样做有两个好处——同一分钟之内无论轮询触发多少次都不会重复响(因为窗口不重叠),而浏览器把后台标签页的定时器节流到每分钟一次、或者系统睡眠把整个进程冻住一段时间之后,恢复时那一跳的窗口会自动变宽,把漏掉的整分钟补回来,而不是像「当前秒数等于 0」那样直接错过。
蜂鸣声是现场合成的,没有任何音频文件。代码用 OscillatorNode 产生一个正弦波,频率固定为 880 Hz——这是十二平均律里的 A5(MIDI 音高 81),由 440 × 2(81−69)/12 算出;拿它当提醒音,是因为 880 Hz 落在听觉最敏感的 2~5 kHz 区间下方一点,穿透力足够又不像高频啸叫那样刺耳。音量由一个 GainNode 控制,包络是「0.0001 → 目标音量 → 0.0001」的指数渐变,单响持续 0.35 秒,然后停 0.25 秒,每 600 毫秒循环一次,所以一分钟内最多响 100 声。
音量 0~100 映射到增益 0~0.5。映射式是 gain = 音量 ÷ 100 × 0.5,所以默认的 60 对应 0.3。上限刻意只到 0.5 而不是 1.0:网页合成音在接近满幅时很容易削波,听起来反而更吵更糊,留一半余量既安全又不失真。音量填 0 时只会亮红色提示条,只是不发声。
音频需要一次用户手势才能解锁。所有现代浏览器都有自动播放限制:页面在没有用户交互前不允许出声,AudioContext 会停在 suspended 状态。本页在你第一次点击任意按钮(包括「添加提醒」和「试听蜂鸣」)时创建并 resume() 音频上下文,之后才会真正发声。这也解释了为什么刚打开页面就撞上整点,可能只弹提示条没有声音。
贪睡不是改写原来那一组,而是临时插入一组。点「贪睡」时,脚本用当前时间加上该组的贪睡分钟数,生成一组新的「仅一次」提醒,标签前面加「贪睡 · 」。原来那组每日提醒保持原样,所以明天同一时间照样会响。
完整示例(数值仅作演示)
示例条件是页面打开时的默认状态:三组提醒分别是 07:00 / 每天 / 贪睡 5 分钟、13:30 / 工作日 / 贪睡 10 分钟、15:00 / 每天 / 不贪睡,音量 60。假设现在是 06:59:30:
- 距离 07:00 还有 0 分 30 秒,「下次响铃」显示「今天 07:00」;
- 这一跳的判定窗口是
(上一跳时间戳, 06:59:31],07:00:00 不落在窗口里,所以不响;等到窗口右端越过 07:00:00 的那一跳才命中,误差上限就是一次轮询间隔 1000 毫秒; - 命中后
OscillatorNode发出 880 Hz 正弦波,单响 0.35 秒,每 0.6 秒一次,直到你点「停止响铃」; - 点「贪睡」则插入一组 07:05 的「仅一次」提醒(5 分钟 = 300 秒),07:00 那组明天照旧;
- 13:30 那组是「工作日」,如果今天是周六,它不会响,会被顺延到下一个周一;
- 三组提醒占用的存储是一段很小的 JSON,只包含
h(小时)、m(分钟)、标签、重复规则、贪睡分钟数和音量。
重复规则、蜂鸣参数与频率对照表
第一张表是四种重复规则的覆盖范围。判断依据是 JavaScript Date.getDay() 的返回值:0 是周日、1 是周一……6 是周六。「工作日」取 1~5,「周末」取 0 和 6。
| 重复规则 | 触发星期 | 每周次数 | 每月约 | 典型用途 |
|---|---|---|---|---|
| 仅一次 | 不限,响过即停用 | 1 | — | 会议、实验、直播前的单次提醒 |
| 每天 | 日 一 二 三 四 五 六 | 7 | 30.0 次 | 起床、吃药、喝水 |
| 工作日 | 一 二 三 四 五 | 5 | 21.4 次 | 上班打卡、站会、午休 |
| 周末 | 六 日 | 2 | 8.6 次 | 周末课程、家庭安排 |
第二张表是蜂鸣用到的音高。所有频率都按十二平均律 f = 440 × 2(MIDI − 69)/12 计算,半音之间的比值是 21/12 ≈ 1.059463。本页只用其中最高那一档。
| 音名 | MIDI | 频率 | 说明 |
|---|---|---|---|
| A4 | 69 | 440.00 Hz | 国际标准音,钢琴中央 C 上方的 A |
| C5 | 72 | 523.25 Hz | 比 A4 高三个半音 |
| E5 | 76 | 659.26 Hz | 常用于提示音的柔和档 |
| G5 | 79 | 783.99 Hz | 接近本页选用的音高 |
| A5(本页) | 81 | 880.00 Hz | 正好是 440 Hz 的两倍,一个周期 1.136 毫秒 |
| C6 | 84 | 1046.50 Hz | 再高一档,部分人会觉得偏尖 |
输入范围、边界与误差说明
- 小时 0~23、分钟 0~59,都必须是整数。填 24 或 60 会报错并指出字段;填小数(如 7.5)同样拒绝——时间是离散刻度,不是把 7.5 当成 07:30。要 07:30 就写小时 7、分钟 30。
- 贪睡 0~30 分钟、音量 0~100(步长 5)。贪睡填 0 时「贪睡」按钮只起到停止作用,不再插入新提醒;音量填 0 则只亮提示条不发声。贪睡 5 分钟连续按 6 次,一共会往后顺延 30 分钟。
- 响铃误差上限是一次轮询间隔,也就是 1000 毫秒。整分钟到达后,最迟在下一跳(不超过 1 秒)被判定命中并开始蜂鸣。这个误差是设计值,不是故障。
- 后台标签页会被节流,但不会漏响。浏览器对隐藏的标签页会降低定时器频率,隐藏时间较长后最严格的情况是每分钟才允许跑一次定时器。因为判定用的是「上一跳到这一跳」的区间而不是「当前是不是整分整秒」,节流后窗口变宽反而会把区间内的触发点补上。
- 页面被关闭、手机锁屏太久导致标签页被丢弃,就不会响。这个闹钟完全跑在页面里,没有 Service Worker、没有系统通知权限,页面一旦不在内存里就停止工作。要长期可靠的提醒请用系统自带的时钟应用。
- 时间取本机时区。代码读的是
new Date()的本地字段(getHours()/getMinutes()),所以显示的就是设备时区的时间。中国大陆全境统一 UTC+8 且没有夏令时,不需要额外换算;如果你手动改了系统时间,显示的时刻会跟着改。 - 声音还受三层外部因素影响。浏览器标签页被静音(标签上有个小喇叭图标)、系统播放设备切到了别的输出、设备静音或音量太低,都会让蜂鸣听不见。红色提示条不受这些影响,仍然会亮。
- 重复规则不支持「每隔 N 天」和「每月某日」。本页只实现四档按星期的规则:每隔 N 天和多组规则叠加会引入时区与跨月判断,容易出难复现的错;需要的话建多条不同时间的提醒即可覆盖大部分场景。
常见问题
关掉页面之后闹钟还会响吗?
不会。本页的全部逻辑跑在当前页面里,关掉标签页或浏览器,计时和蜂鸣就一起停了,也不会留下任何后台进程。需要「关掉也响」的场景请用手机或电脑自带的时钟应用,它们由操作系统调度。
标签页切到后台,会不会错过整点?
不会漏。判定用的是「上一跳的时间戳到这一跳的时间戳」这个半开区间,浏览器把隐藏标签页的定时器节流到每分钟一次之后,那一跳的窗口会覆盖被节流的整段时间,区间里落着的触发点会被补上。代价是响铃的准点性变差,最多可能晚几十秒——但不会不响。
为什么刚打开页面时到点只亮提示条、不出声?
浏览器的自动播放策略要求页面先收到一次用户交互,AudioContext 才能从 suspended 变成 running。本页在你第一次点任意按钮时创建并恢复音频上下文,之后才会真正发声。想提前确认,可以点一次「试听蜂鸣」。
点「贪睡」会改掉原来那组提醒吗?
不会。贪睡是在「当前时间 + 该组的贪睡分钟数」处插入一组新的「仅一次」提醒,标签前面加上「贪睡 · 」,原来那组提醒的重复规则完全不动,明天同一时间照响。贪睡填 0 的提醒按下去就只是停止当前这次响铃。
一次会响多久?怎么停下来?
一直响,直到你点「停止响铃」,或者关掉页面。循环周期是 600 毫秒:0.35 秒的发声加 0.25 秒的静音,也就是每分钟最多 100 声。这样设计的目的是避免「响一声你没听见就过去了」,但正常使用时建议尽快点停止。
为什么用每秒轮询,而不是直接算好时间用 setTimeout?
因为 setTimeout 只保证「不早于」目标时间,页面被冻结或系统休眠后会一次性延迟触发,醒来时可能已经在几分钟之后,且没有机会补响。轮询加区间判定既不会因为定时器抖动而漏掉,也不会因为页面恢复而重复触发,行为更好预测。
蜂鸣为什么定在 880 Hz?能换成别的音吗?
880 Hz 是十二平均律的 A5,正好是标准音 A4 的两倍频。它落在人耳较敏感的区域下方一点,穿透办公环境的背景噪声足够,又不像 2 kHz 以上那样容易让人烦躁。本页把它写死为一个常数,所有提醒共用;想区分不同的组,可以给每组设不同音量。
音量填 100 是不是最大声?和系统音量什么关系?
不是。音量 0~100 只映射到网页音频的增益 0~0.5(100 对应 0.5),最终响多大还要乘上系统的输出音量。这样映射是为了留一半余量避免削波失真。想让声音更大,请调设备的音量键,或者把播放设备切到音箱。