1
0

カウントダウンを繰り返すタイマーを作った話【JavaScript】

Last updated at Posted at 2024-09-13

この世にはルーチンタイマーという素晴らしいアプリが存在します。

(※この記事を書いた人≠このアプリの作者)
みなさん、この世にはこのようなアプリがある事をご存知ですか?

(しばらく「このアプリは何?」って人向けの説明&布教活動です。知っている方は読み飛ばしてください)

このアプリは一言で言うと、『指定された時間のカウントダウンタイマーを、指定した分だけ連続で実行できるもの』です。

例をあげます。
あなたの朝の予定がこんな感じだとします。

朝の予定(理想):スマホのアラームを止めて起床→トイレ(5分)→洗顔(5分)→朝ご飯(20分)→着替え(10分)→歯磨き(5分)→家を出る
朝の予定(現実):スマホのアラームを止めて起床→スマホを見る(30分)→洗顔(2分半)→朝ご飯(5分)→着替え(5分)→歯磨き兼トイレ(2分半)→家を出る

後者の場合、スマホを見る時間が無駄ですよね。
でも忙しい時のネットサーフィン程魅惑的なものはないですよね。
誰かが「おい時間やべぇぞ!」って言ってくれなきゃ永遠にやってしまう。
でも頭を言葉で殴って現実に戻してくれる人は居ない。

このアプリは、そんな人の味方です。
もしあなたが理想の朝の予定を過ごしたい場合、5分→5分→20分→10分→5分の順でカウントダウンタイマーを設定する事ができます。こんな風に。

そして、別の画面でこの設定したタイマーを実行すると、「今から【タスクの時間】、【タスクの名前】を実行します」というアナウンスと共にタイマーが始まります。
タイマーの周りの円の色が変わる事によって、あとどのくらいの時間が残っているのかわかる親切仕様。

真ん中の「| |」ボタンが一時停止&再開で、「<」ボタンや「>」ボタンを押すと前後のタイマーに移動します。
また、タイマーが切れると次のタスクへ自動で移行します。(例:開始してから5分が過ぎると自動でトイレから洗顔の表示になり、タイマーが5分に戻る)便利!

また、タイマーが半分になると「残り半分です」、次のタイマーへ移行する5秒前に「残り5秒です」というアナウンスが入ります。タイマーから目をそらしていても安心ですね。

このアプリすごく便利なんだけど……

さてこのアプリ、こんな素晴らしい機能を宿していながら、欠点があります。
PC版が存在しないのです!

じゃあ自分で作るしか無いじゃない!!

作るか!!!JavaScriptで!!

と、言うわけで(タイマー部分だけ)作りました。
まだまだ荒削りですが、本当に最低限の機能だけでも完成したので、自分の中で一区切りつける為にアップしました。

↓同じものをGitHubにあげています。

ちなみに見た目はこうです。
image.png

うっわだっさ!!!!

main.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>main</title>
    <link href="css/main.css" rel="stylesheet" />
    <script src="js/main.js"></script>
</head>
<body>
      <!-- ルーチンタイマー -->
      <div id="timer-container">
        <div id="timer-label">トイレ</div> 
        <div id="timer-display">05:00</div>
        <div>
          <button id="prev-button" class="timer-button"></button>
    <button id="control-button" class="timer-button">スタート</button>
    <button id="next-button" class="timer-button"></button>
        </div>
  <script src="script.js"></script>
</body>
</html>

main.css
/* タイマー全体のコンテナ */
#timer-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh; /* 画面の高さ全体を使用 */
  font-family: Arial, sans-serif;
  margin: 0; /* 外部マージンをリセット */
}

/* タイマーのラベル */
#timer-label {
  font-size: 24px;
  margin-bottom: 10px;
}

/* タイマーの表示 */
#timer-display {
  font-size: 48px;
  margin-bottom: 20px;
}

/* タイマーのボタン */
.timer-button {
  padding: 10px 20px;
  font-size: 16px;
  cursor: pointer;
  margin: 5px;
}

main.js
document.addEventListener('DOMContentLoaded', function() {
  // タイマーの表示領域とボタン要素を取得
  const timerDisplay = document.getElementById('timer-display');
  const timerLabel = document.getElementById('timer-label');
  const controlButton = document.getElementById('control-button');
  const prevButton = document.getElementById('prev-button');
  const nextButton = document.getElementById('next-button');

  // タイマーの時間とラベルを設定
  const timeIntervals = [
    { minutes: 5, seconds: 0, label: 'トイレ' },
    { minutes: 5, seconds: 0, label: '洗顔' },
    { minutes: 20, seconds: 0, label: '朝ごはん' },
    { minutes: 10, seconds: 0, label: '着替え' },
    { minutes: 5, seconds: 0, label: '歯磨き' }
  ];

  let currentIntervalIndex = 0;  // 現在のタイマーインデックスを保持
  let countdownInterval;         // setIntervalで使用するタイマーのID
  let isPaused = true;           // タイマーが一時停止しているかどうかを管理するフラグ
  let remainingTime;             // 残り時間を管理する変数

  // 音声ファイルの定義(1階層上のmusicフォルダ内にあるファイル。そこに残り5秒前からのカウントダウン音声を入れています。)
  //一番上のはいきなり喋り始めるとびっくりするのでそれを防ぐ用
  const pianoSound = new Audio('../PCroutine/music/Piano02-1(Short).mp3'); 
  const fiveSound = new Audio('../PCroutine/music/5.wav');
  const fourSound = new Audio('../PCroutine/music/4.wav');
  const threeSound = new Audio('../PCroutine/music/3.wav');
  const twoSound = new Audio('../PCroutine/music/2.wav');
  const oneSound = new Audio('../PCroutine/music/1.wav');
  const zeroSound = new Audio('../PCroutine/music/終了.wav');
  const otsukaresama1 = new Audio('../PCroutine/music/お疲れ様でした.wav');
  const otsukaresama2 = new Audio('../PCroutine/music/タスクを全て終えま....wav');

  // タイマーを開始する関数
  function startTimer(duration) {
    let timer = duration;
    remainingTime = duration;

    // 最初にタイマーの表示を即座に更新する
    updateTimerDisplay(timer);

    // 1秒ごとに実行されるカウントダウン処理
    countdownInterval = setInterval(function () {
      if (!isPaused) { // タイマーが動作中の場合のみ進行
        if (--timer >= 0) {
          updateTimerDisplay(timer);
          // 残り時間に応じて音声を再生する
          if (timer === 6) {
            pianoSound.play();  
          } else if (timer === 5) {
            fiveSound.play();  
          } else if (timer === 4) {
            fourSound.play();  
          } else if (timer === 3) {
            threeSound.play(); 
          } else if (timer === 2) {
            twoSound.play();   
          } else if (timer === 1) {
            oneSound.play();   
          } else if (timer === 0) {
            zeroSound.play();   
          }
        } else {
        //タイマーを停止&次のタイマーに移行
          clearInterval(countdownInterval); 
          currentIntervalIndex++; 

          // 次のタイマーが存在する場合は開始、すべて終了したらメッセージ表示
          if (currentIntervalIndex < timeIntervals.length) {
            startNextTimer(); // 次のタイマーを開始
          } else {
            timerDisplay.textContent = 'お疲れ様でした'; // 全てのタイマー終了時にメッセージ表示
            controlButton.textContent = 'スタート'; // ボタンをスタートに戻す
            isPaused = true; // タイマーを停止状態に戻す
            // ランダムにお疲れ様メッセージを再生
            // これも1階層上のmusicフォルダ内にあるファイルです
            const randomSound = Math.random() < 0.5 ? otsukaresama1 : otsukaresama2;
            randomSound.play();
          }
        }
        remainingTime = timer; // 残り時間を更新
      }
    }, 1000); // 1秒ごとに実行
  }

  // タイマーの表示を更新する関数
  function updateTimerDisplay(timer) {
    let minutes = parseInt(timer / 60, 10); 
    let seconds = parseInt(timer % 60, 10);

    // 分と秒を2桁に整形(例: 05:09)
    minutes = minutes < 10 ? '0' + minutes : minutes;
    seconds = seconds < 10 ? '0' + seconds : seconds;

    // タイマーの表示を更新
    timerDisplay.textContent = minutes + ':' + seconds;
  }

  // 現在のタイマーラベルを更新する関数
  function updateLabel() {
    timerLabel.textContent = timeIntervals[currentIntervalIndex].label; 
  }

  // 次のタイマーを開始する関数
  function startNextTimer() {
    updateLabel(); // タイマーラベルを更新
    const nextInterval = timeIntervals[currentIntervalIndex]; 
    const duration = nextInterval.minutes * 60 + nextInterval.seconds; 計算
    startTimer(duration); 
  }

  // 「スタート/ストップ」ボタンがクリックされたときの処理
  controlButton.addEventListener('click', function() {
  // 一時停止中であればタイマーを再開
    if (isPaused) { 
    //最初のタイマーがまだ動作していない場合、開始
      if (currentIntervalIndex === 0 && !countdownInterval) { 
        startNextTimer();
      }
      isPaused = false; 
      controlButton.textContent = 'ストップ'; 
      // タイマー動作中の場合、一時停止
    } else { 
      isPaused = true;
      controlButton.textContent = 'スタート';
    }
  });

  // 「<」ボタンがクリックされたときの処理(前のタイマーに戻る)
  prevButton.addEventListener('click', function() {
  // 前のタイマーが存在する場合、前のタイマーに移動し、前のタイマーを開始
    if (currentIntervalIndex > 0) { 
      clearInterval(countdownInterval);
      currentIntervalIndex--;
      updateLabel(); 
      const prevInterval = timeIntervals[currentIntervalIndex]; 
      const duration = prevInterval.minutes * 60 + prevInterval.seconds; 
      startTimer(duration); 
      isPaused = false; 
      controlButton.textContent = 'ストップ'; 
    }
  });

  // 「>」ボタンがクリックされたときの処理(次のタイマーに進む)
  // 「<」ボタンがクリックされた時の処理とほとんど同じ
  nextButton.addEventListener('click', function() {
    if (currentIntervalIndex < timeIntervals.length - 1) { 
      clearInterval(countdownInterval); 
      currentIntervalIndex++; 
      updateLabel(); 
      const nextInterval = timeIntervals[currentIntervalIndex]; 
      const duration = nextInterval.minutes * 60 + nextInterval.seconds; 
      startTimer(duration); 
      isPaused = false; 
      controlButton.textContent = 'ストップ'; 
    }
  });
});


今後やりたい事

  • タイマーの残り時間に合わせたスクロールバーを用意する
  • タイマーが切り替わる時用の音声を用意&再生できるようにする(例:朝ごはんのタイマーが開始する時に「今から、朝ごはんの時間です」と言う)
  • タイマーが半分になった時用の音声を用意&再生できるようにする
  • 全てのタイマーを終えた後、最初のタイマーに戻れるようにする(現時点だとページ再読み込みしないと戻れない)
  • とんでもなくダサいので見た目をなんとかする
  • 複数のタイマーを用意&好きなタイマーを選んで動かせるようにする
  • todoリストを作成し、このタイマーと合体させる
  • Java等のプログラミング言語を使用してタイマーやtodoリストの情報をDBに格納できるようにする
  • スマホ版を用意し、PC版と内容を共有できるようにする

 と、いうのも、普段私はこのタイマーを朝の予定としてではなく、以下のようなルーチンを守る用に使っています。(なお、ご飯等のある程度時間の決まっているイベントは以下のルーチンに組み込んでいません)

勉強15分
 内容:資格習得の勉強、語学学習の勉強、プログラミングの勉強…などなど

(きりの良いところで区切るなり終えるなりして次の予定に切り替える準備用の5分)

自由15分
 内容:ソシャゲ、積みゲー消化、積読消化、その他やりたい事…などなど

(きりの良いところで区切るなり終えるなりして次の予定に切り替える準備用の5分)

作業15分
 内容:家事全般、ペットの世話、明日の準備、その他勉強でも遊びでもない作業の消化…などなど

(きりの良いところで区切るなり終えるなりして次の予定に切り替える準備用の5分)

勉強(以下ループ)

 それで、タイマーが勉強の時は勉強する内容をチェックリストで出して、自由の時は……といったようにしたいなと考えております。

 …
 ……
 やることがいっぱいだぁ…頑張るぞ…

追記

Googleで『カウントダウンタイマー とは』で検索するとタイマーが出てくるのですね。
知らなかった。
image.png

なお、『カウントダウンタイマー』のみで検索すると出てこない模様。
どうして…。
image.png

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0