Содержание статьи
от Синтии Ли
Я начал свое путешествие freeCodeCamp в декабре 2017 года и мне не хватает двух проектов, чтобы получить сертификат Front-End Development. Эта публикация документирует мой процесс завершения проекта Pomodoro Clock.
Что такое часы 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", "https://www.soundjay.com/misc/sounds/bell-ringing-05.mp3");
После нажатия кнопки «Пуск» интервал очищается. Новый интервал устанавливается, если isPaused
изменения от правда к ошибочный.
Кнопка «сбросить» очищает интервал и сбрасывает переменные.
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 функция таймера здесь происходит магия обратного отсчета. Из него вычитается одна секунда секунд. Если секунд; 0, воспроизводится будильник, и функция определяет, следует ли следующий обратный отсчет быть рабочим сеансом или сеансом перерыва.
function timer() { seconds --; if (seconds < 0) { clearInterval(countdown); alarm.currentTime = 0; alarm.play(); seconds = (isBreak ? breakTime : workTime) * 60; isBreak = !isBreak; }}
Теперь пора поработать над кнопками +/- для продолжительности работы и перерыва. Сначала я создал onclick
функция каждой кнопки. Несмотря на то, что он был функциональным, безусловно, было место для усовершенствования.
document.querySelector("#work-plus").onclick = function() { workDuration < 60 ? workDuration += increment : workDuration; }document.querySelector("#work-minus").onclick = function() { workDuration > 5 ? workDuration -= increment : workDuration; }document.querySelector("#break-plus").onclick = function() { breakDuration < 60 ? breakDuration += increment : breakDuration; }document.querySelector("#break-minus").onclick = function() { breakDuration > 5 ? breakDuration -= increment : breakDuration; }
Тот же Redditor предложил мне использовать ассоциативный массив, который, по сути, является набором пар ключ-значения.
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]; }}
Пора обновить HTML!
Я создал функции для обновления дисплея обратного отсчета и кнопок и включил эти функции в общую функцию, которая также обновила статус и продолжительность работы/перерыва.
Наконец-то я использовал document.onclick
запустить функция updateHTML каждый раз, когда пользователь нажимает на страницу. Я также использовал window.setInterval
запускать функцию 10 раз в секунду.
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;
И это результат моего проекта!
Вы можете просмотреть мой окончательный проект здесь.
Последние мнения
Мой наибольший вывод из этого проекта заключается в том, что я должен стремиться к простоте с точки зрения дизайна кода, поскольку это предпосылка для надежности. Это сделает мой код легким для понимания, легкой настройки и легкого обновления.
Мне также напоминают о преимуществах парного программирования и просмотра кода, особенно если вы новичок в программировании.
Надо еще многому научиться. Но пока позвольте мне вознаградить себя тарелкой Pasta al pomodoro.