【ポモドーロタイマーを作ろうVanillaJS編③】~ポモドーロサイクル処理の実装①~

前回からの続きになります。

前回は1秒ごとにカウントダウンを行い
それをタイマー領域に描画するところまで実装しました。

今回は、作業と休憩のサイクルを自動的に回す実装を行っていきます。

イメージはこのような感じです。

workから自動的にbreakに切り替わっています。


f:id:hnm-n-1029:20200704120053g:plain
自動サイクルイメージ


※これから説明するコードは私が一番最初に実装したコードです。
同じ処理を繰り返し書くなど、美しくないコードになっています。
まずは動くことを優先させて組み立てていきました。
後々、こうした方がいいなと思う部分が多々あり
リファクタリングしておりますので、参考にはならないかと思います。。。
すみません!
自分のために備忘として残しておきたいという意図です。

作業と休憩のサイクルを自動的に回すには


私は次のように考えました。


①作業、小休憩、長休憩の各ターンでcountDown()関数を作り
 残り時間が0になったタイミングでif文により
 次のターンのcountDown()関数を呼び出す。
 ※そもそもこの考えが良くないですね。同じ処理を書いてます。


②if文で条件分岐できるように、各ターンでstateを持たせる。
state:"work" or "break" or "longBreak"


③長休憩に入る時の状態を識別するために、
 小休憩が終わるごとにcycleCountを1づつ増やす。
 設定した回数に到達した時に、長休憩を呼び出す

●作業中:workTimeCountDown()
●小休憩中:breakTimeCountDown()
●長休憩中:longBreakTimeCountDown()


この考えに基づいて書いたコードがこちらです。
※長くなるので代表でworkTimeCountDown()のみを記載します。

  const workTime = 25;
  const breakTime = 5;
  const longBreakTime = 30;

  let state = "work"; //初期状態
  let startTime;
  let workTimeoutId;
  let breakTimeoutId;
  let longBreakTimeoutId;
  let elapsedTime = 0; //以下で説明します
  let cycleCount = 3;  //長い休憩までの作業回数
  let count = 1;

  function workTimeCountDown() {
    state = "work";
    const remainigTime = (workTime * 60 *1000) - elapsedTime - (Date.now() - startTime);
    const restMin = String(Math.floor((remainigTime / 1000 / 60) % 60)).padStart(2, '0');
    const restSec = String(Math.floor((remainigTime / 1000) % 60)).padStart(2, '0');
        workTimeoutId = setTimeout(() => {
        workTimeCountDown();
        }, 1000); 
      } else if ( remainigTime < 0 && count === cycleCount+1 ) {
        clearTimeout(workTimeoutId);
        startTime = Date.now();
        elapsedTime = 0;
        longBreakTimeCountDown();
        timeState.textContent = state;
      } else {
      clearTimeout(workTimeoutId);
      startTime = Date.now();
      elapsedTime = 0;
      breakTimeCountDown();
      timeState.textContent = state;
    }
  }

同じような内容を小休憩と長休憩でも書いています.....

よく考えたらCountDownに引数として設定時間(作業、小休憩、長休憩)を渡せば
各ターンでのcountDown関数を作る必要はないんですよね。
それに気づかずにわざわざ関数を作っておりました。。。。勉強が足りない。。。


各ボタンのクリック処理の実装


続いて、START、STOP、RESETボタンのクリック処理の実装部分です。

各ボタンの処理はstateの状態でswitchにより評価しています。

  start.addEventListener('click', () => {
    startTime = Date.now();
    workTimeCountDown();
    switch (state) {
      case "work": workTimeCountDown();
        break;
      case "break": breakTimeCountDown();
        break;
      case "longBreak": longBreakTimeCountDown();
        break;
    }
  });

   stop.addEventListener('click', () => {
    switch (state) {
      case "work": clearTimeout(workTimeoutId);
        break;
      case "break": clearTimeout(breakTimeoutId);
        break;
      case "longBreak": clearTimeout(longBreakTimeoutId);
        break;
    }
    elapsedTime += Date.now() - startTime;
  });

  reset.addEventListener('click', () => {
    switch (state) {
      case "work": timer.textContent = `${String(workTime).padStart(2, '0')}:00`;
        break;
      case "break": timer.textContent = `${String(breakTime).padStart(2, '0')}:00`;
        break;
      case "longBreak": timer.textContent = `${String(longBreakTime).padStart(2, '0')}:00`;
        break;
    }
    elapsedTime = 0;
  });

elapsedTimeについて


stopボタンをクリックした際の

「elapsedTime += Date.now() - startTime;」

は一時停止の機能を正しく実行するために必要となります。

elapsedTimeには、startTimeから一時停止するまでの時間が保持されています。
リスタートした際に、その値を引いてあげることで、
止まった時間からカウントダウンを再開することができます。
※elapsedTimeを考慮しないと時間が巻き戻ってしまう現象が起こります


今回はここまでになります。

美しくないコードですが、これからきれいにできるように日々精進です!


↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック

kenjimorita.jp