Wie ich meine Pomodoro Clock App erstellt habe und welche Lektionen ich dabei gelernt habe

Ich habe meine freeCodeCamp-Reise im Dezember 2017 begonnen und bin zwei Projekte, die das Front-End-Entwicklungszertifikat noch nicht abgeschlossen haben. Dieser Beitrag dokumentiert meinen Prozess des Abschlusses des Pomodoro Clock-Projekts.

Was ist eine Pomodoro-Uhr?

Die Pomodoro-Technik ist ein ebenso einfaches wie effektives Zeitmanagement-Framework. Sie verwenden einen Timer, um Ihre Arbeit in Zeitblöcke (normalerweise 25 Minuten) zu unterteilen, die durch eine Pause von 5 Minuten voneinander getrennt sind. Nach jeweils 4 Pomodoros können Sie eine längere Pause einlegen.

Ich musste folgende User Stories erfüllen:

  • Ich kann einen 25-minütigen Pomodoro starten, und der Timer läuft ab, sobald 25 Minuten verstrichen sind.
  • Ich kann die Uhr für meinen nächsten Pomodoro zurücksetzen.
  • Ich kann die Länge jedes Pomodoro anpassen.

Gestaltungs Entwurf

Mein Konstruktionsprinzip ist es, die Benutzeroberfläche sauber und einfach zu halten. Ich mochte die Idee, eine Tomate als Timer zu verwenden. Es gibt eine Arbeits- / Pausenanzeige, einen Timer-Countdown und eine Wiedergabe- / Pause-Taste.

Unterhalb des Timers hatte ich Einstellungen zum Ändern der Arbeits- und Pausenzeit und eine Reset-Taste.

Layoutprobleme, auf die ich gestoßen bin

Ich hatte große Probleme damit, das Tomatenbild unter den anderen Elementen im Hintergrund zu positionieren. Wie ich wünschte, es gäbe eine Layoutoption, die ich auswählen könnte! ?

Ein Vorschlag, den ich gefunden habe, war, das Tomatenbild auf meiner bevorzugten Hintergrundfarbe als neues Bild zu speichern und dieses Bild dann im Hintergrund zu verwenden. Der Nachteil dabei war, dass es nach dem Testen der Layout-Reaktionsfähigkeit wackelig aussah.

Am Ende habe ich es geschafft, es richtig zu machen absolute positioning, indem ich die Prozentsätze und topund leftgeändert habe transform.

#status { position: absolute; top: 45%; left:50%; transform: translate(-50%, -50%);}
.timerDisplay { position: absolute; top: 60%; left: 50%; transform: translate(-50%, -50%);}
#start-btn { position: absolute; bottom: 8%; left: 48%; transform: translate(-50%, -50%);}

Die unteren Einstellungen waren ziemlich einfach. Ich habe CSS Grid verwendet, um die Komponenten in drei Spalten zu unterteilen, wobei die mittlere Spalte die halbe Breite der äußeren Spalten hat.

.settings { margin: auto; width: 80%; display: grid; grid-template-columns: 2fr 1fr 2fr; align-items: center;}

Wieder einmal habe ich transformdie Reset-Taste zur besseren Ausrichtung verschoben.

Meinen Code strukturieren - und dann auseinander reißen

Ich finde es hilfreich, meine Codestruktur zu erstellen, wenn ich die Anforderungen aufschlüssle:

  • Der Timer schaltet zwischen Starten und Anhalten um, wenn ich auf die Schaltfläche "Start" klicke.
  • Sobald der Timer Null erreicht, wird ein Alarm ausgelöst.
  • Auf eine Arbeitssitzung folgt immer eine Pausensitzung.
  • Die Arbeits- und Pausenzeiten können geändert werden.
  • Die 'Reset'-Taste setzt (Sie haben es erraten) den Timer zurück.

Ich hatte zuvor eine Countdown-Uhr im Wes Bos JavaScript30-Kurs absolviert, sodass ich wusste, dass ich die setIntervalMethode verwenden kann. Ich habe mich auch dazu entschlossen, mich an Vanille-JavaScript zu halten und mich nicht auf jQuery zu verlassen.

Und so fing ich an, meinen JavaScript-Code zu schreiben. Obwohl ich es geschafft habe, eine funktionierende Pomodoro-Uhr zu erstellen, werde ich hier nicht die erste Version meines Codes durchgehen. Dies liegt daran, dass ich wesentliche Änderungen daran vorgenommen habe, nachdem ich konstruktives Feedback von einem erstaunlichen Fremden auf Reddit erhalten habe. ?

Ja, auf Reddit passieren schöne Dinge!

Die Hauptpunkte des Feedbacks waren:

  • setInterval(timer, 1000)Das Auslösen dauert mindestens 1000 ms, kann jedoch länger dauern. Sie sollten also überprüfen, wie viel Zeit tatsächlich vergangen ist, oder Ihre Uhr kann ungenau sein.
  • Gruppieren Sie alle HTML-Updates in einem Abschnitt, da dies das Aktualisieren und Debuggen Ihres Codes erleichtert.
  • Im Allgemeinen ist es eine gute Idee, den Code zu erstellen, ohne an die Darstellung zu denken.
  • Achten Sie auf die Logik des Timers und entfernen Sie unnötigen Code.
  • Stellen Sie sicher, dass die Variablennamen beschreibend sind. Hinterlassen Sie bei Bedarf Kommentare.

Sie können mein erstes Commit auf GitHub anzeigen.

Mein Code umgestalten

Nachdem ich all dieses wertvolle Feedback erhalten hatte, überarbeitete ich meinen Code mehrmals, bis ich damit zufrieden war.

Zuerst habe ich alle Variablen definiert. Da ich jQuery nicht verwendete, stellte ich sicher, dass ich alle meine Elemente mit jQuery erfasste document.querySelector.

let countdown = 0; // variable to set/clear intervalslet seconds = 1500; // seconds left on the clocklet workTime = 25;let breakTime = 5;let isBreak = true;let isPaused = true;
const status = document.querySelector("#status");const timerDisplay = document.querySelector(".timerDisplay");const startBtn = document.querySelector("#start-btn");const resetBtn = document.querySelector("#reset");const workMin = document.querySelector("#work-min");const breakMin = document.querySelector("#break-min");

Als nächstes habe ich das Audioelement erstellt.

const alarm = document.createElement('audio'); alarm.setAttribute("src", "//www.soundjay.com/misc/sounds/bell-ringing-05.mp3");

Wenn Sie auf die Schaltfläche "Start" klicken, wird das Intervall gelöscht. Ein neues Intervall wird festgelegt, wenn isPausedvon true zu false gewechselt wird .

Die Schaltfläche 'Zurücksetzen' löscht das Intervall und setzt die Variablen zurück.

startBtn.addEventListener('click', () => { clearInterval(countdown); isPaused = !isPaused; if (!isPaused) { countdown = setInterval(timer, 1000); }})
resetBtn.addEventListener('click', () => { clearInterval(countdown); seconds = workTime * 60; countdown = 0; isPaused = true; isBreak = true;})

The timer function is where the countdown magic happens. It deducts one second from seconds. If seconds <; 0, the alarm is played, and the function determines if the next countdown should be a work session or break session.

function timer() { seconds --; if (seconds < 0) { clearInterval(countdown); alarm.currentTime = 0; alarm.play(); seconds = (isBreak ? breakTime : workTime) * 60; isBreak = !isBreak; }}

Now it’s time to work on the +/- buttons for the work and break durations. Initially, I created an onclick function for every button. While it was functional, there was definitely room for improvement.

document.querySelector("#work-plus").onclick = function() { workDuration  5 ? workDuration -= increment : workDuration; }document.querySelector("#break-plus").onclick = function() { breakDuration  5 ? breakDuration -= increment : breakDuration; }

That same kind Redditor suggested that I use an associative array, which is essentially a set of key value pairs.

let incrementFunctions = {"#work-plus": function () { workTime = Math.min(workTime + increment, 60)}, "#work-minus": function () { workTime = Math.max(workTime - increment, 5)}, "#break-plus": function () { breakTime = Math.min(breakTime + increment, 60)}, "#break-minus": function () { breakTime = Math.max(breakTime - increment, 5)}};
for (var key in incrementFunctions) { if (incrementFunctions.hasOwnProperty(key)) { document.querySelector(key).onclick = incrementFunctions[key]; }}

It’s time to update the HTML!

I created functions to update the countdown display and button display, and incorporated those functions into an overarching function that also updated the Work/Break status and durations.

Finally, I used document.onclick to run the updateHTML function everytime the user clicks on the page. I also used window.setInterval to run the function 10 times a second for good measure.

function countdownDisplay() { let minutes = Math.floor(seconds / 60); let remainderSeconds = seconds % 60; timerDisplay.textContent = `${minutes}:${remainderSeconds < 10 ? '0' : ''}${remainderSeconds}`;}
function buttonDisplay() { if (isPaused && countdown === 0) { startBtn.textContent = "START"; } else if (isPaused && countdown !== 0) { startBtn.textContent = "Continue"; } else { startBtn.textContent = "Pause"; }}
function updateHTML() { countdownDisplay(); buttonDisplay(); isBreak ? status.textContent = "Keep Working" : status.textContent = "Take a Break!"; workMin.textContent = workTime; breakMin.textContent = breakTime;}
window.setInterval(updateHTML, 100);
document.onclick = updateHTML;

And that’s the wrap up of my project!

You can view my final project here.

Final thoughts

My biggest takeaway from this project is that I should aim for simplicity in terms of code design, because it is a prerequisite for reliability. It will make my code easy to understand, easy to debug, and easy to update.

I am also reminded of the benefits of paired programming and code reviews, especially when one is new to coding.

There is still so much to learn. But for now, let me reward myself with a plate of Pasta al pomodoro.