Як я створив свій додаток Pomodoro Clock та уроки, які я вивчив у дорозі

У грудні 2017 року я розпочав мандрівку безкоштовною програмою CodeCamp і не хочу заповнювати сертифікат розвитку Front-End. Цей пост документує мій процес завершення проекту годинника Pomodoro.

Що таке годинник Pomodoro?

Техніка Pomodoro - це система управління часом, яка настільки проста, наскільки ефективна - ви використовуєте таймер, щоб розбити вашу роботу на часові блоки (зазвичай 25 хвилин), розділені 5-хвилинною перервою. Після кожних 4 помодоросів ви можете робити довшу перерву.

Мені довелося виконати такі історії користувачів:

  • Я можу розпочати 25-хвилинний помодоро, і таймер спрацює, коли пройде 25 хвилин.
  • Я можу скинути годинник для мого наступного помодоро.
  • Я можу налаштувати довжину кожного помодоро.

Дизайн / макет

Мій принцип дизайну - підтримувати інтерфейс користувача чистим і простим. Мені сподобалася ідея використовувати помідор як таймер. Є індикація роботи / перерви, зворотний відлік часу та кнопка відтворення / паузи.

Під таймером я мав налаштування для зміни тривалості роботи та перерви, а також кнопку скидання.

Проблеми з макетом, з якими я стикався

У мене були основні проблеми з розміщенням помідорного зображення на задньому плані під іншими елементами. Як би я хотів, щоб був варіант макету, який я міг би вибрати! ?

Однією з пропозицій, яку я знайшов, було зберегти зображення помідора у вибраному кольорі тла як нове зображення, а потім використовувати це зображення у фоновому режимі. Недоліком цього було те, що він почав виглядати химерно, як тільки я перевірив чуйність макета.

Врешті-решт, мені вдалося це правильно зробити за допомогою комбінації absolute positioning, модифікації topі leftвідсотків, і 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%);}

Нижні налаштування були досить простими. Я використав CSS Grid для розділення компонентів на три стовпці, середній стовпець дорівнює половині ширини зовнішніх стовпців.

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

Ще раз, я використовував transformкнопку перезавантаження для кращого вирівнювання.

Структурування мого коду - а потім його розривання

Я вважаю корисним придумати свою структуру коду, якщо я розбиваю вимоги:

  • Таймер буде перемикатися між запуском та паузою, коли я натискаю кнопку "старт".
  • Як тільки таймер досягне нуля, спрацює будильник.
  • Після робочого сеансу завжди йде перерва.
  • Тривалість роботи та перерви можна змінити.
  • Кнопка `` скинути '' (як ви вже здогадалися) скине таймер.

Раніше я закінчив годинник зворотного відліку на курсі Wes Bos JavaScript30, тому знав, що можу скористатися цим setIntervalметодом. Я також вирішив кинути виклик собі, дотримуючись ванільного JavaScript, і уникати покладання на jQuery.

І ось я почав писати свій код JavaScript. Хоча мені вдалося створити функціональний годинник pomodoro, я не буду тут переглядати першу версію свого коду. Це тому, що я вніс у нього суттєві зміни після отримання конструктивних відгуків від дивовижного незнайомця на Reddit. ?

Так, на Reddit трапляються приємні речі!

Основними моментами відгуків були:

  • setInterval(timer, 1000)для спрацьовування потрібно щонайменше 1000 мс, але може знадобитися більше часу. Тож слід перевірити, скільки часу фактично минуло, інакше ваш годинник може бути неточним.
  • Групуйте всі оновлення HTML в одному розділі, оскільки це полегшує оновлення та налагодження коду.
  • Як правило, це гарна ідея створювати код, не думаючи про представлення взагалі.
  • Будьте впевнені в логіці таймера і позбудьтеся зайвого коду.
  • Переконайтесь, що імена змінних описові. Залишайте коментарі, коли це необхідно.

Ви можете переглянути мій перший коміт на GitHub.

Рефакторинг мого коду

Отримавши всі ці цінні відгуки, я кілька разів переробляв свій код, поки не був задоволений ним.

Спочатку я визначив усі змінні. Оскільки я не використовував jQuery, я переконався, що захопив усі свої елементи за допомогою 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");

Далі я створив звуковий елемент.

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

Після натискання кнопки «старт» інтервал очищається. Встановлюється новий інтервал, якщо значення isPausedзмінюється з true на false .

Кнопка "скидання" очищає інтервал і скидає змінні.

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.