作ったもの
Cook Timer — https://sen.ltd/portfolio/cook-timer/
- 無制限の同時タイマー
- ラベルと所要時間をタイマーごとに設定
- クイックプリセット(3, 5, 7, 10, 15, 30, 60 分)
- タイムスタンプベース追跡(タブスロットリングでもドリフトしない)
- Web Audio API のビープ音
- Notification API の通知
- Wake Lock API で画面スリープ防止
- localStorage 永続化
vanilla JS、ゼロ依存、ビルド不要。node --test で 50 ケース。
setInterval カウンタの罠
// 間違い: バックグラウンドでドリフトする
setInterval(() => { remaining -= 1000; }, 1000);
Chrome はバックグラウンドタブのコールバックを1 分に 1 回まで絞る。10 分タイマーをセットしてタブを切り替えると、10 分後に戻ってきたときにはまだ「9 分残り」と表示される。
タイムスタンプで計算
timer.startedAt = Date.now();
function getRemaining(timer) {
const elapsed = Date.now() - timer.startedAt + timer.pausedElapsed;
return Math.max(0, timer.duration - elapsed);
}
コールバックの頻度に関係なく、壁時計時間から残り時間を計算。レンダーが何回走ろうと正確。
一時停止の会計
pausedElapsed にこれまでの累積経過を記録。再開時は startedAt を「今」にリセットし、getRemaining で (now - startedAt) + pausedElapsed を差し引く。複数回の一時停止でも正確。
Wake Lock API
wakeLock = await navigator.wakeLock.request('screen');
最初のタイマーが走り始めたら取得、最後のタイマーが止まったら解放。非対応ブラウザはフォールバック(画面に触れてもらう)。
シリーズ
100+ 公開ポートフォリオ シリーズの #52 です。
- 📦 リポジトリ: https://github.com/sen-ltd/cook-timer
- 🌐 デモ: https://sen.ltd/portfolio/cook-timer/
- 🏢 会社: https://sen.ltd/
