当前位置: 首页 > 工具软件 > pomodoro > 使用案例 >

我如何构建Pomodoro Clock应用程序,以及在此过程中学到的课程

程鸿煊
2023-12-01

by Cynthia Lee

辛西娅·李(Cynthia Lee)

我如何构建Pomodoro Clock应用程序,以及在此过程中学到的课程 (How I built my Pomodoro Clock app, and the lessons I learned along the way)

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项目的过程。

什么是番茄钟? (What is a 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:

我必须满足以下用户故事:

  • I can start a 25 minute pomodoro, and the timer will go off once 25 minutes has elapsed.

    我可以开始一个25分钟的番茄,一旦25分钟过去,计时器就会关闭。
  • I can reset the clock for my next pomodoro.

    我可以为下一个番茄钟重置时钟。
  • I can customise the length of each pomodoro.

    我可以自定义每个番茄的长度。

设计/布局 (Design / layout)

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.

在计时器下方,我有一些设置可以修改工作和休息时间,还有一个重置按钮。

我遇到的版面设计问题 (Layout issues I encountered)

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 ,修改topleft百分比以及进行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按钮,以更好地对齐。

构建我的代码-然后将其拆开 (Structuring my code - and then tearing it apart)

I find it helpful to come up with my code structure if I breakdown the requirements:

如果分解要求,我发现提出我的代码结构会有所帮助:

  • The timer will toggle between starting and pausing when I click the ‘start’ button.

    当我单击“开始”按钮时,计时器将在开始和暂停之间切换。
  • Once the timer reaches zero, an alarm will go off.

    一旦计时器达到零,警报将响起。
  • A work session is always followed by a break session.

    工作会议之后总是有休息会议。
  • The work and break durations can be modified.

    工时和休息时间可以修改。
  • The ‘reset’ button will (you guessed it) reset the timer.

    “重置”按钮将(您猜对了)重置计时器。

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毫秒才能触发,但可能需要更长的时间。 因此,您应该检查实际花费了多少时间,否则您的时钟可能不准确。

  • Group all HTML updates in one section, as this makes your code easier to update and debug.

    将所有HTML更新分组在一个部分中,因为这使您的代码更易于更新和调试。
  • It is generally a good idea to make the code without thinking about the representation at all.

    通常,完全不考虑表示形式的情况下编写代码是一个好主意。
  • Be sure about the logic of the timer, and get rid of unnecessary code.

    确保计时器的逻辑,并清除不必要的代码。
  • Make sure the variable names are descriptive. Leave comments when necessary.

    确保变量名称具有描述性。 必要时发表评论。

You can view my first commit on GitHub.

您可以在GitHub上查看我的第一次提交。

重构我的代码 (Refactoring my code)

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.

单击“开始”按钮后,间隔将被清除。 如果isPausedtrue更改为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.

您可以在这里查看我的最终项目。

最后的想法 (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.

还有很多东西要学。 但是现在,让我用一盘意大利面来奖励自己。

翻译自: https://www.freecodecamp.org/news/how-i-built-my-pomodoro-clock-app-and-the-lessons-i-learned-along-the-way-51288983f5ee/

 类似资料: