<!DOCTYPE html>
Learning Vue Part 3: Building a Pomodoro Timer
<br> body {<br> font-family: sans-serif;<br> margin: 0;<br> padding: 20px;<br> }<br> h1, h2, h3 {<br> margin-top: 30px;<br> }<br> pre {<br> background-color: #f5f5f5;<br> padding: 10px;<br> border-radius: 5px;<br> overflow-x: auto;<br> }<br> img {<br> max-width: 100%;<br> display: block;<br> margin: 20px auto;<br> }<br> code {<br> background-color: #f0f0f0;<br> padding: 2px 5px;<br> border-radius: 3px;<br> }<br>
Learning Vue Part 3: Building a Pomodoro Timer
Welcome back to our Vue.js learning journey! In this part, we'll dive into building a practical application: a Pomodoro timer. The Pomodoro Technique is a time management method using a timer to break down work into intervals, separated by short breaks. It's a popular technique for improving focus and productivity.
This tutorial will guide you through creating a basic Pomodoro timer using Vue.js. We'll cover essential concepts like:
- Data binding and reactivity
- Methods and event handling
- Conditional rendering
- Timers and intervals
- Component structure
Let's get started!
Setting up the Project
If you haven't already, create a new Vue.js project using the Vue CLI:
vue create pomodoro-timer
Choose the default preset and install the necessary dependencies.
Creating the Timer Component
We'll create a new component named
Timer.vue
inside the
src/components
directory.
├── src
│ ├── components
│ │ └── Timer.vue
│ ├── main.js
│ └── App.vue
├── babel.config.js
├── public
│ └── index.html
├── package.json
└── vue.config.js
The contents of
Timer.vue
will be:
<template>
<div class="timer-container">
<h1>
Pomodoro Timer
</h1>
<div class="time-display">
{{ formattedTime }}
</div>
<button @click="startTimer">
Start
</button>
<button @click="pauseTimer">
Pause
</button>
<button @click="resetTimer">
Reset
</button>
</div>
</template>
<script>
export default {
name: 'Timer',
data() {
return {
timeRemaining: 25 * 60, // 25 minutes in seconds
timerInterval: null,
};
},
computed: {
formattedTime() {
const minutes = Math.floor(this.timeRemaining / 60);
const seconds = this.timeRemaining % 60;
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
},
},
methods: {
startTimer() {
this.timerInterval = setInterval(() => {
this.timeRemaining--;
if (this.timeRemaining <= 0) {
this.stopTimer();
// Handle timer completion (e.g., switch to break)
}
}, 1000);
},
pauseTimer() {
clearInterval(this.timerInterval);
this.timerInterval = null;
},
resetTimer() {
this.timeRemaining = 25 * 60;
this.stopTimer();
},
stopTimer() {
clearInterval(this.timerInterval);
this.timerInterval = null;
},
},
};
</script>
<style scoped="">
.timer-container {
display: flex;
flex-direction: column;
align-items: center;
}
.time-display {
font-size: 3em;
margin-bottom: 20px;
}
</style>
Here's a breakdown of the code:
-
Template:
Defines the HTML structure of the timer component. -
Data:
-
: Stores the time remaining in seconds, initialized to 25 minutes.
timeRemaining
-
: Stores the interval ID for the timer.
timerInterval
-
-
Computed:
-
: Formats the
formattedTime
into a human-readable format (minutes:seconds).
timeRemaining
-
-
Methods:
-
: Starts the timer by setting the interval to decrement
startTimer
every second.
timeRemaining
-
: Pauses the timer by clearing the interval.
pauseTimer
-
: Resets the
resetTimer
to its initial value and stops the timer.
timeRemaining
-
: Stops the timer (used by both pause and reset).
stopTimer
-
-
Style (scoped):
Defines basic CSS for the timer component.
Integrating the Timer into App.vue
Now, let's use the
Timer
component in the main
App.vue
file:
<template>
<div id="app">
<timer>
</timer>
</div>
</template>
<script>
import Timer from './components/Timer.vue';
export default {
name: 'App',
components: {
Timer,
},
};
</script>
This imports the
Timer
component and registers it for use in the
App
component. Now, when you run the app with
npm run serve
, you'll see the basic Pomodoro timer.
Adding Break Functionality
Let's enhance the timer by adding a break functionality after each work session:
<template>
<div class="timer-container">
<h1>
Pomodoro Timer
</h1>
<div class="time-display">
{{ formattedTime }}
</div>
<button @click="startTimer">
Start
</button>
<button @click="pauseTimer">
Pause
</button>
<button @click="resetTimer">
Reset
</button>
</div>
</template>
<script>
export default {
// ... (rest of the component code)
data() {
return {
timeRemaining: 25 * 60, // 25 minutes in seconds
timerInterval: null,
isWorkSession: true, // Flag to track work/break session
};
},
methods: {
// ... (other methods)
startTimer() {
// ... (timer start logic)
if (this.timeRemaining <= 0) {
this.stopTimer();
this.isWorkSession = !this.isWorkSession; // Switch session type
if (this.isWorkSession) {
this.timeRemaining = 25 * 60; // Reset work time
} else {
this.timeRemaining = 5 * 60; // Set break time
}
}
},
// ... (other methods)
},
};
</script>
We've introduced a new data property,
isWorkSession
, to track whether we're in a work session or a break. When the timer reaches zero, it toggles this flag and resets the time based on the current session type.
Adding UI Enhancements
Let's improve the UI to provide better feedback to the user.
-
Visualizing Session Type:
We can display the current session type (Work or Break) next to the timer. -
Progress Bar:
A progress bar can visually represent the time elapsed.
- Displaying Session Type
Modify the template in
Timer.vue
:
<template>
<div class="timer-container">
<h1>
Pomodoro Timer
</h1>
<div class="time-display">
{{ formattedTime }}
<span v-if="isWorkSession">
(Work)
</span>
<span v-else="">
(Break)
</span>
</div>
<button @click="startTimer">
Start
</button>
<button @click="pauseTimer">
Pause
</button>
<button @click="resetTimer">
Reset
</button>
</div>
</template>
We use conditional rendering with
v-if
to display "Work" or "Break" based on
isWorkSession
.
- Adding a Progress Bar
We can use a simple
div
as a progress bar. We'll calculate its width based on the remaining time and the total time of the session.
<template>
<div class="timer-container">
<h1>
Pomodoro Timer
</h1>
<div :style="{ width: `${progressBarWidth}%` }" class="progress-bar">
</div>
<div class="time-display">
{{ formattedTime }}
<span v-if="isWorkSession">
(Work)
</span>
<span v-else="">
(Break)
</span>
</div>
<button @click="startTimer">
Start
</button>
<button @click="pauseTimer">
Pause
</button>
<button @click="resetTimer">
Reset
</button>
</div>
</template>
<script>
export default {
// ... (rest of the component code)
computed: {
// ... (other computed properties)
progressBarWidth() {
const sessionTime = this.isWorkSession ? 25 * 60 : 5 * 60; // Total session time
return ((sessionTime - this.timeRemaining) / sessionTime) * 100;
},
},
// ... (rest of the component code)
};
</script>
<style scoped="">
.timer-container {
display: flex;
flex-direction: column;
align-items: center;
}
.time-display {
font-size: 3em;
margin-bottom: 20px;
}
.progress-bar {
background-color: #ccc;
height: 10px;
width: 0%;
margin-bottom: 10px;
border-radius: 5px;
}
</style>
We've added a new computed property
progressBarWidth
, which calculates the width of the progress bar. The width is calculated as a percentage of the time elapsed compared to the total session time. The
:style
binding in the template updates the width dynamically based on
progressBarWidth
.
Now, you have a basic Pomodoro timer with a progress bar and session type indicator. Feel free to add more features and customize the UI according to your preferences.
Adding Audio Notifications
To enhance the user experience, we can add audio notifications for the start, pause, and completion of the timer.
First, you need to include audio files for these events. You can find free sound effects online or create your own. For this example, let's assume you have the following audio files in the
public
directory:
-
start.mp3
-
pause.mp3
-
complete.mp3
Now, modify the
Timer.vue
component to play these audio files in the appropriate methods:
<script>
export default {
// ... (rest of the component code)
methods: {
// ... (other methods)
startTimer() {
// ... (timer start logic)
const audio = new Audio('/start.mp3');
audio.play();
},
pauseTimer() {
// ... (timer pause logic)
const audio = new Audio('/pause.mp3');
audio.play();
},
stopTimer() {
// ... (timer stop logic)
if (this.timeRemaining <= 0) {
const audio = new Audio('/complete.mp3');
audio.play();
}
},
// ... (other methods)
},
};
</script>
In each relevant method, we create a new
Audio
object, load the appropriate audio file, and play it using the
play()
method. Make sure the audio paths are correct in the
Audio
constructor.
Now, your Pomodoro timer will play sounds to indicate the start, pause, and completion of the timer.
Conclusion
In this tutorial, we've learned how to build a basic Pomodoro timer using Vue.js. We covered core concepts like data binding, methods, computed properties, conditional rendering, and event handling. We also explored ways to enhance the UI and add audio notifications.
Remember, this is just a starting point. You can customize and extend this timer to your liking. You can add features like:
- Settings for work/break durations
- Multiple work sessions before a longer break
- Persistence to save progress
- Integration with other productivity tools
As you continue your Vue.js journey, you'll find that these fundamental concepts are applicable to a wide range of applications. Keep experimenting and have fun building with Vue!