ポモドーロタイマーJavaScriptプログラミング自作

JavaScriptでポモドーロタイマーを作る方法|25分・5分のカウントダウンを実装

JavaScriptでポモドーロタイマーを作る方法を解説します。HTML・CSSと組み合わせて、25分集中・5分休憩、開始、一時停止、リセット、集中と休憩の切り替えを実装します。

JavaScriptでポモドーロタイマーを作る方法|25分・5分のカウントダウンを実装

「JavaScriptでポモドーロタイマーを作りたい」「25分集中・5分休憩のカウントダウンを実装したい」。

ポモドーロタイマーは、JavaScriptの練習題材として作りやすいWebアプリです。

基本的なポモドーロでは、

25分集中 → 5分休憩

を繰り返します。

最初は、

  • 残り時間の表示
  • 開始
  • 一時停止
  • リセット

だけでも十分です。

その後、

  • 集中と休憩の切り替え
  • 長い休憩
  • 終了音
  • 自動開始
  • ブラウザ通知
  • 状態保存

などを追加できます。

この記事では、HTML・CSS・JavaScriptを使ってシンプルなポモドーロタイマーを作る方法を紹介します。

JavaScriptで作るポモドーロタイマー

今回作るタイマーでは、

集中:25分

休憩:5分

を設定します。

最低限必要な機能は、

  • 開始
  • 一時停止
  • リセット
  • 集中と休憩の切り替え

です。

まずはシンプルな構成で作り、その後に機能を追加していきます。

HTMLを用意する

最初にタイマー画面を作ります。

<div class="pomodoro">
  <p id="mode">集中</p>

  <div id="time">25:00</div>

  <div class="buttons">
    <button id="start">開始</button>
    <button id="pause">一時停止</button>
    <button id="reset">リセット</button>
  </div>
</div>

modeには現在の状態、

timeには残り時間を表示します。

CSSでタイマーを見やすくする

最低限のCSSを追加します。

.pomodoro {
  max-width: 400px;
  margin: 80px auto;
  text-align: center;
}

#mode {
  font-size: 24px;
}

#time {
  margin: 24px 0;
  font-size: 64px;
  font-weight: bold;
}

button {
  padding: 10px 16px;
  margin: 4px;
}

ポモドーロタイマーでは、残り時間が最も重要です。

まずは数字を大きく表示して、状態を分かりやすくします。

25分を秒で管理する

JavaScriptでは、時間を秒で管理すると扱いやすくなります。

25分は、

25 × 60 = 1500秒

です。

5分は、

5 × 60 = 300秒

です。

定数として、

const FOCUS_TIME = 25 * 60;
const BREAK_TIME = 5 * 60;

と定義します。

現在の残り時間は、

let timeLeft = FOCUS_TIME;

として持ちます。

集中と休憩の状態を管理する

現在が集中なのか休憩なのかも管理します。

let mode = "focus";

例えば、

focus
break

の2種類で管理できます。

集中時間が終了したらbreakへ切り替え、休憩が終わったらfocusへ戻します。

分と秒に変換する

残り時間は秒で持っていますが、画面では、

25:00

のように表示したいところです。

そこで、表示用の関数を作ります。

function formatTime(totalSeconds) {
  const minutes = Math.floor(totalSeconds / 60);
  const seconds = totalSeconds % 60;

  return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
}

例えば、

formatTime(1500);

なら、

25:00

になります。

画面を更新する

残り時間やモードを画面へ反映する関数も作ります。

function updateDisplay() {
  document.getElementById("time").textContent =
    formatTime(timeLeft);

  document.getElementById("mode").textContent =
    mode === "focus" ? "集中" : "休憩";
}

初期表示でも一度実行します。

updateDisplay();

setIntervalでタイマーを動かす

JavaScriptでは、setInterval()を使って一定間隔で処理を実行できます。

今回なら、1秒ごとに残り時間を減らします。

let timerId = null;

function startTimer() {
  if (timerId !== null) {
    return;
  }

  timerId = setInterval(() => {
    timeLeft -= 1;

    updateDisplay();
  }, 1000);
}

これで1秒ごとに、

25:00
24:59
24:58

と減っていきます。

開始ボタンとつなげる

開始ボタンを取得します。

const startButton = document.getElementById("start");

クリックしたら、

startButton.addEventListener("click", startTimer);

とします。

これで「開始」を押すとタイマーが動きます。

一時停止する

次に一時停止を作ります。

function pauseTimer() {
  clearInterval(timerId);
  timerId = null;
}

停止してもtimeLeftはそのまま残ります。

そのため、

12:35

で一時停止し、再び開始すると、

12:35

から続けられます。

ボタンへ登録します。

document
  .getElementById("pause")
  .addEventListener("click", pauseTimer);

リセットする

リセットでは、

  • タイマー停止
  • 集中モードへ戻す
  • 25分へ戻す

という処理を行います。

function resetTimer() {
  clearInterval(timerId);
  timerId = null;

  mode = "focus";
  timeLeft = FOCUS_TIME;

  updateDisplay();
}

ボタンにも登録します。

document
  .getElementById("reset")
  .addEventListener("click", resetTimer);

0秒で止める

今のままだと、

00:00

を過ぎても、

-1
-2
-3

と減ってしまいます。

そこで、タイマー処理に条件を追加します。

timerId = setInterval(() => {
  timeLeft -= 1;

  if (timeLeft <= 0) {
    timeLeft = 0;
    clearInterval(timerId);
    timerId = null;
  }

  updateDisplay();
}, 1000);

これで0秒で止まります。

集中が終わったら休憩へ切り替える

ポモドーロタイマーでは、25分終了後に5分休憩へ切り替えます。

切り替え用の関数を作ります。

function switchMode() {
  if (mode === "focus") {
    mode = "break";
    timeLeft = BREAK_TIME;
  } else {
    mode = "focus";
    timeLeft = FOCUS_TIME;
  }

  updateDisplay();
}

タイマーが0になったらswitchMode()を実行します。

自動で集中と休憩を切り替える

タイマー処理を変更します。

function startTimer() {
  if (timerId !== null) {
    return;
  }

  timerId = setInterval(() => {
    timeLeft -= 1;

    if (timeLeft <= 0) {
      switchMode();
    }

    updateDisplay();
  }, 1000);
}

これで、

25分集中

5分休憩

25分集中

と切り替わります。

完成したJavaScript

ここまでをまとめると、次のようになります。

const FOCUS_TIME = 25 * 60;
const BREAK_TIME = 5 * 60;

let mode = "focus";
let timeLeft = FOCUS_TIME;
let timerId = null;

function formatTime(totalSeconds) {
  const minutes = Math.floor(totalSeconds / 60);
  const seconds = totalSeconds % 60;

  return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
}

function updateDisplay() {
  document.getElementById("time").textContent =
    formatTime(timeLeft);

  document.getElementById("mode").textContent =
    mode === "focus" ? "集中" : "休憩";
}

function switchMode() {
  if (mode === "focus") {
    mode = "break";
    timeLeft = BREAK_TIME;
  } else {
    mode = "focus";
    timeLeft = FOCUS_TIME;
  }
}

function startTimer() {
  if (timerId !== null) {
    return;
  }

  timerId = setInterval(() => {
    timeLeft -= 1;

    if (timeLeft <= 0) {
      switchMode();
    }

    updateDisplay();
  }, 1000);
}

function pauseTimer() {
  clearInterval(timerId);
  timerId = null;
}

function resetTimer() {
  clearInterval(timerId);
  timerId = null;

  mode = "focus";
  timeLeft = FOCUS_TIME;

  updateDisplay();
}

document
  .getElementById("start")
  .addEventListener("click", startTimer);

document
  .getElementById("pause")
  .addEventListener("click", pauseTimer);

document
  .getElementById("reset")
  .addEventListener("click", resetTimer);

updateDisplay();

これで基本的なポモドーロタイマーとして使えます。

長い休憩を追加する

一般的なポモドーロでは、集中を数回行ったあとに長い休憩を取ります。

例えば、

4回集中

15分休憩

です。

まず、

const LONG_BREAK_TIME = 15 * 60;

を追加します。

完了した集中回数も管理します。

let completedPomodoros = 0;

集中が終了したときに、

completedPomodoros += 1;

と増やします。

4回目だけ長い休憩にする

モードを、

focus
shortBreak
longBreak

の3種類にすると分かりやすくなります。

例えば、

function switchMode() {
  if (mode === "focus") {
    completedPomodoros += 1;

    if (completedPomodoros % 4 === 0) {
      mode = "longBreak";
      timeLeft = LONG_BREAK_TIME;
    } else {
      mode = "shortBreak";
      timeLeft = BREAK_TIME;
    }

    return;
  }

  mode = "focus";
  timeLeft = FOCUS_TIME;
}

とできます。

これで4回目の集中終了後だけ長い休憩になります。

25分以外へ変更できるようにする

実際に使うタイマーなら、時間設定も変更できると便利です。

例えばHTMLに、

<label>
  集中時間
  <input id="focusMinutes" type="number" value="25">
</label>

を追加します。

JavaScriptでは、

const minutes =
  Number(document.getElementById("focusMinutes").value);

として取得できます。

秒に直すなら、

const seconds = minutes * 60;

です。

終了音を鳴らす

タイマー終了時に音を鳴らす方法もあります。

例えば、

<audio id="alarm" src="/alarm.mp3"></audio>

を用意して、

document.getElementById("alarm").play();

とします。

ただし、ブラウザでは音声の自動再生に制限があります。

実際に公開する場合は、ユーザー操作やブラウザ仕様を考慮して実装しましょう。

ブラウザ通知を使う

別タブで作業していると、タイマー終了に気づかない場合があります。

その場合はWeb Notifications APIを使って、

集中時間が終了しました

などの通知を表示する方法があります。

ただし、通知にはユーザーの許可が必要です。

通知機能を使うユーザーが操作したタイミングで許可を求める設計にすると分かりやすいでしょう。

タブタイトルに残り時間を表示する

PCで使うなら、ブラウザタブに残り時間を表示すると便利です。

updateDisplay()に、

document.title =
  `${formatTime(timeLeft)} | ${mode === "focus" ? "集中" : "休憩"}`;

を追加します。

例えば、

18:42 | 集中

と表示できます。

別タブで作業していても時間を確認しやすくなります。

setIntervalは必ず正確な1秒ではない

簡単なタイマーならsetInterval()で十分ですが、実用的なタイマーでは注意が必要です。

ブラウザがバックグラウンドになったり、端末がスリープしたりすると、処理が正確に1秒間隔で実行されない場合があります。

単純に、

timeLeft -= 1;

だけで管理すると、実際の時刻とタイマーがずれる可能性があります。

終了予定時刻を使う

より実用的にするなら、終了予定時刻を保存して残り時間を計算する方法があります。

例えば、

let endTime = Date.now() + timeLeft * 1000;

として終了時刻を持ちます。

残り時間は、

const remaining = Math.max(
  0,
  Math.ceil((endTime - Date.now()) / 1000)
);

と計算します。

この方法なら、setInterval()の呼び出しが少し遅れても、現在時刻から残り時間を再計算できます。

localStorageで状態を保存する

ページを再読み込みすると、通常のJavaScript変数は初期化されます。

タイマーを復元したいなら、localStorageへ状態を保存できます。

例えば、

  • 現在のモード
  • 終了予定時刻
  • 集中時間
  • 休憩時間
  • 完了セット数

などです。

localStorage.setItem(
  "pomodoroState",
  JSON.stringify({
    mode,
    endTime,
    completedPomodoros
  })
);

のように保存できます。

自動開始を追加する

ポモドーロでは、

集中終了

休憩へ切り替え

自動で休憩開始

という動作にすることもできます。

さらに、

休憩終了

次の集中開始

まで自動化できます。

ただし、自動開始が苦手な人もいるため、公開サービスなら設定でオン・オフできるようにすると使いやすくなります。

スキップ機能を追加する

現在のタイマーを途中で終了し、次のモードへ移るスキップも便利です。

例えば、

function skipTimer() {
  switchMode();
  updateDisplay();
}

のような処理を作れます。

実際には、自動開始の設定や完了セット数との関係も考えて設計します。

JavaScriptだけでも十分作れる

ポモドーロタイマーを作るために、必ずReactやVueなどのフレームワークが必要なわけではありません。

基本的なタイマーなら、

HTML

CSS

JavaScript

だけで作れます。

DOM操作やイベント処理を学びたい場合は、むしろ素のJavaScriptで一度作ってみると仕組みを理解しやすいでしょう。

Reactで作る場合

タイマーをより大きなWebアプリへ発展させたい場合は、Reactを使う方法もあります。

Reactなら、

  • useState
  • useEffect
  • useRef
  • Custom Hook

などを使って状態やタイマー処理を管理できます。

Reactで作りたい場合は、Reactでポモドーロタイマーを作る方法も参考にしてください。

GitHubでJavaScriptの実装を探す

ほかの人の実装を参考にしたい場合は、GitHubで、

pomodoro timer javascript

と検索する方法があります。

同じJavaScriptでも、

  • setIntervalで管理
  • 終了予定時刻で管理
  • classで管理
  • モジュールへ分割

など、さまざまな実装があります。

公開コードを利用する場合は、必ずライセンスを確認しましょう。

詳しくは、GitHubでポモドーロタイマーを探す方法も参考にしてください。

ポモドーロタイマーを自作する

JavaScript以外にも、ReactやPython、電子工作などでポモドーロタイマーを自作できます。

まずどんな機能を作るか整理したい場合は、ポモドーロタイマーを自作する方法も参考にしてください。

最初はシンプルに作る

最初から、

  • ログイン
  • 統計
  • タスク管理
  • クラウド同期
  • BGM
  • 複数テーマ

まで作る必要はありません。

まず、

25分を表示する

開始する

一時停止する

リセットする

5分休憩へ切り替える

ところまで完成させましょう。

そこから一つずつ追加したほうが、バグが起きた場所も特定しやすくなります。

実際のポモドーロタイマーを触ってみる

自分で作る前に、既存のポモドーロタイマーを使ってみるのも参考になります。

Pomolikaは、ブラウザで使えるポモドーロタイマーです。

例えば、

  • 開始
  • 停止
  • 再開
  • リセット
  • スキップ
  • 集中と休憩の切り替え

など、実際の操作を確認できます。

自分でJavaScriptのタイマーを作るときのUIや機能設計を考える参考にもなります。

Pomolikaのタイマーを使う

まとめ

JavaScriptを使えば、HTML・CSSと組み合わせてポモドーロタイマーを作れます。

基本的な流れは、

  1. 25分を秒へ変換する
  2. 残り時間を変数で管理する
  3. setInterval()で表示を更新する
  4. 一時停止でclearInterval()する
  5. 0になったら休憩へ切り替える
  6. 休憩終了後に集中へ戻す

です。

基本機能ができたら、

  • 長い休憩
  • セット数
  • 時間変更
  • 終了音
  • ブラウザ通知
  • タブタイトル
  • 自動開始
  • localStorage

などを追加できます。

また、実用的なタイマーを作る場合は、

1秒ごとに単純に残り時間を減らすだけでなく、終了予定時刻と現在時刻との差から残り時間を計算する

方法も検討するとよいでしょう。

まずはHTML・CSS・JavaScriptだけでシンプルな25分・5分タイマーを完成させ、そこから必要な機能を追加していくのがおすすめです。