【ポモドーロタイマーを作ろうVanillaJS編③】~ポモドーロサイクル処理の実装①~
前回からの続きになります。
前回は1秒ごとにカウントダウンを行い
それをタイマー領域に描画するところまで実装しました。
今回は、作業と休憩のサイクルを自動的に回す実装を行っていきます。
イメージはこのような感じです。
workから自動的にbreakに切り替わっています。
※これから説明するコードは私が一番最初に実装したコードです。
同じ処理を繰り返し書くなど、美しくないコードになっています。
まずは動くことを優先させて組み立てていきました。
後々、こうした方がいいなと思う部分が多々あり
リファクタリングしておりますので、参考にはならないかと思います。。。
すみません!
自分のために備忘として残しておきたいという意図です。
作業と休憩のサイクルを自動的に回すには
私は次のように考えました。
①作業、小休憩、長休憩の各ターンで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を考慮しないと時間が巻き戻ってしまう現象が起こります
今回はここまでになります。
美しくないコードですが、これからきれいにできるように日々精進です!
↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック