by Cynthia Lee
辛西娅·李(Cynthia Lee)
I embarked on my freeCodeCamp journey in Dec 2017, and am two projects shy of completing the Front-End Development Certificate. This post documents my process of completing the Pomodoro Clock project.
我于2017年12月开始了我的freeCodeCamp之旅,并且是两个未完成前端开发证书的项目。 这篇文章记录了我完成Pomodoro Clock项目的过程。
The Pomodoro Technique is a time-management framework which is as simple as it is effective - you use a timer to break your work into time blocks (usually 25 minutes), separated by a 5 minute break. After every 4 pomodoros, you can take a longer break.
Pomodoro技术是一个时间管理框架,它非常有效,它很简单-您使用计时器将工作分为多个时间段(通常为25分钟),每个时间段间隔为5分钟。 每4个番茄一次,您可以休息更长的时间。
I had to fulfil the following user stories:
我必须满足以下用户故事:
My design principle is to keep the user interface clean and simple. I loved the idea of using a tomato as the timer. There is a work/break display, timer countdown, and a play/pause button.
我的设计原则是保持用户界面干净简单。 我喜欢使用番茄作为计时器的想法。 有工作/休息时间显示,计时器倒计时和播放/暂停按钮。
Below the timer, I had settings to modify the work and break duration, and a reset button.
在计时器下方,我有一些设置可以修改工作和休息时间,还有一个重置按钮。
I had major issues with getting the tomato image positioned in the background under the other elements. How I wish there was a layout option I could select! ?
在将番茄图像放置在其他元素下的背景中时,我遇到了主要问题。 我多么希望有一个我可以选择的布局选项! ?
One suggestion I found was to save the tomato image on my preferred background colour as a new image, then use that image in the background. The downside to that was that it started to look wonky once I tested the layout responsiveness.
我发现的一个建议是将番茄图像以我喜欢的背景颜色保存为新图像,然后在背景中使用该图像。 不利的一面是,一旦我测试了布局响应能力,它就开始显得古怪。
In the end, I managed to get it right with a combination of absolute positioning
, modifying the top
and left
percentages, and transform
.
最后,我设法通过结合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%);}
The bottom settings were fairly straightforward. I used CSS Grid to separate the components into three columns, with the middle column being half the width of the outer columns.
底部设置非常简单。 我使用CSS Grid将组件分为三列,中间的列是外部列的宽度的一半。
.settings { margin: auto; width: 80%; display: grid; grid-template-columns: 2fr 1fr 2fr; align-items: center;}
Once again, I used transform
to shift the reset button for better alignment.
再次,我使用了transform
来移动Reset按钮,以更好地对齐。
I find it helpful to come up with my code structure if I breakdown the requirements:
如果分解要求,我发现提出我的代码结构会有所帮助:
I had previously completed a countdown clock in the Wes Bos JavaScript30 course, so I knew that I could use the setInterval
method. I also decided to challenge myself by sticking to vanilla JavaScript, and avoid relying on jQuery.
我之前已经在Wes Bos JavaScript30课程中完成了倒数计时,所以我知道我可以使用setInterval
方法。 我还决定通过坚持使用普通JavaScript来挑战自己,并避免依赖jQuery。
And so I started writing my JavaScript code. While I managed to create a functional pomodoro clock, I won’t go through the first version of my code here. This is because I made significant changes to it after receiving constructive feedback from an amazing stranger on Reddit. ?
因此,我开始编写我JavaScript代码。 虽然我设法创建了一个功能性的番茄钟,但在这里我不会浏览代码的第一个版本。 这是因为在收到Reddit上一个令人惊讶的陌生人的建设性反馈后,我对其进行了重大更改。 ?
Yes, nice things do happen on Reddit!
是的,Reddit上确实发生了很多事情!
The main points of the feedback were:
反馈的要点是:
setInterval(timer, 1000)
takes at least 1000 ms to trigger, but may take longer. So you should check how much time actually elapsed, or your clock can be inaccurate.
setInterval(timer, 1000)
至少需要1000毫秒才能触发,但可能需要更长的时间。 因此,您应该检查实际花费了多少时间,否则您的时钟可能不准确。
You can view my first commit on GitHub.
您可以在GitHub上查看我的第一次提交。
After getting all that valuable feedback, I refactored my code several times until I was satisfied with it.
得到所有有价值的反馈后,我对代码进行了多次重构,直到对它满意为止。
First, I defined all the variables. As I was not using jQuery, I ensured that I captured all my elements using document.querySelector
.
首先,我定义了所有变量。 因为我没有使用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");
Next, I created the audio element.
接下来,我创建了音频元素。
const alarm = document.createElement('audio'); alarm.setAttribute("src", "https://www.soundjay.com/misc/sounds/bell-ringing-05.mp3");
When the ‘start’ button is clicked, the interval is cleared. A new interval is set if isPaused
changes from true to false.
单击“开始”按钮后,间隔将被清除。 如果isPaused
从true更改为false,则会设置一个新间隔。
The ‘reset’ button clears the interval, and resets the variables.
“重置”按钮清除间隔,并重置变量。
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.
计时器功能是发生倒计时魔术的地方。 它从秒中减去一秒 。 如果秒< ; 0,将发出警报,并且该功能确定下一个倒数是工作时段还是休息时段。
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.
现在是时候使用+/-按钮进行工作和休息时间了。 最初,我为每个按钮创建了一个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; }
That same kind Redditor suggested that I use an associative array, which is essentially a set of key value pairs.
同一种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]; }}
It’s time to update the HTML!
现在是时候更新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.
最后,每次用户单击页面时,我都使用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;
And that’s the wrap up of my project!
这就是我的项目的总结!
You can view my final project here.
您可以在这里查看我的最终项目。
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.
还有很多东西要学。 但是现在,让我用一盘意大利面来奖励自己。