5 years of DevFest CountDown - Part 3
This article is the third of series ( 5 years of DevFest CountDown - Part 1 and 5 years of DevFest CountDown - Part 2)
2018
The idea
2018 was the year of the “space” theme. The idea was to throw planets around the sun to create a constellation of avatars! Each attendee could log through the app and use its avatar as a planet by throwing it with a very simple interface.
You simply drag your finger from your avatar to the center of the screen and when you release it, your planet will be thrown to the main screen!
A New year = a new challenge
As I use the countdown as a personal challenge to try lots of things, this year my biggest challenge was to learn a new framework. I wanted to learn Vue.js. I used the package @vue/cli:3.0.0
to serve, build my project and I used the version 2.5.x which was the latest version at the moment of this project.
Since the last 6 months, I started to be exhausted by what we can call the “CLI fatigue”. Indeed, as the frameworks evolve every month, their CLI often evolve too and when you work with several projects with different versions, having a CLI in a specific version could be a problem… I simply install the cli as a devDependencies
and reference the CLI in the script part of my package. Here is, for example, the package.json of my project
{ |
As you can see, I referenced the cli I needed and I was sure that the version of vue/cli wouldn’t interfere with any other of my cli.
The new Architecture
As you can see, I removed lots of elements to focus on my code. And I wanted to go back to something compatible with the KISS principle
As firebase evolves each year, in 2016, when I started to use it, Firestore wasn’t available and we couldn’t listen to change on the tree. Back in 2016, I decided to use the realtime database. But in 2018, firestore offered me all I need :
- A database with a higher quota for the storage: 1 GB
- A number of simultaneous connection very high 1,000,000
- The possibility to be notified when a change is done to the tree (even if in realtime, we could do that)
It was more than I really needed, so I used it in replacement of realtime database.
One of the pain point I had to face this year was to be attentive to the performances!! Indeed, I wanted to show a high number of planets on screen. Every planet is following an ellipse and I have to calculate for each planet if it enter in collision with another planet. All those calculations could cost times so I decided to use a Web Worker to do all the calculations and to notify the app with a new model as soon as the calculations were done.
To summarize:
- A player launchs a planet
- The planet is added/updated in the firestore tree
- The Countdown screen is notified and asks to the webworker to add the new planet
In parallel
- The webworker calculates the position of the planets, the collisions, updates the model
- The Countdown screen receives the data.
- When the
requestAnimationFrame
is called, the Countdown screen reads the current model and displays it
All the animations, stars, shine effect, are just maths and effects with the Canvas, I won’t explain how I do this. If you are interested in that, check the source code (end of this section).
Data Structure and security
To secure my paths and data, I used firebase authentication and path configuration in Vue:
const secureRoute = (to, from, next) => { |
The idea was to check for specific route (/countdown
, /game
) if the user is authenticated. If not, I redirect the user to the authentication route. The period while the application is waiting for checking if the user is authenticated, I redirect him/her/them to a waiting screen. I’m not sure if it’s the best practice or not but it works pretty well 😇.
The /countdown
route should be shown only to “Admins” so I secured this page with this redirection
// Mount method of my CountDown component |
Indeed, I can consider that an error here is thrown when the current user has not the permission to see the collection “admins”. This leads me to the protection of the data. Here is the structure of my data
// Collection 'admins' |
To secure those data, I used these firebase rules:
service cloud.firestore { |
With these few lines, I secured my application and my data 💪.
Few enhancements
Although every year I reuse the codebase for the timer, the audio player, … This year I wanted to fix and enhance a little bit the class. I focused my enhancement to the Audio Player and the timer.
The Timer
Before this year, I never had to create a class for it, this has to be fixed.
; |
This timer is updated very often and gives the delta to the main screen.
The Audio player
One of our problem each year is the timing. We play music and we want a specific music to be played at the end. Let me explain it more easily. If we start the countdown 45min before the beginning of the Keynote and we want the last song to be played to be, for example, ACDC - Thunderstock and we want the countdown to show 00:00 when Thunderstock is finishing… It was not very easy because we have to calculate the right time to start our playlist, be sure to not stop it, …
So I got an idea. What if I can specify what is the last song, its duration and what if the countdown automatically switched to this song when it’s the right moment? That’s why I did an evolution in my AudioPlayer class
|
I had some controls to deal with the sound volume and to switch to the playlist of last songs. The code that execute those controls is in a separate class because it’s not the rule of the AudioPlayer to know when to change! The code that dealt with the timing was in my CountDown component:
const timeBeforeLastSongs = 60 * 1000; // 1 Minute |
My Conclusion
My first challenge was to try Vue.js and my conclusion is VueJS is good framework / library to prototype application but not the best solution when you have an application with a high frame rate like 30fps. The main problem comes from the fact that my data were refreshed very often… More often than the inner mechanism of rendering of Vue. So Vue destroys and recreates too many times the HTML Nodes.
For example, to be performant, I had to change this code:
<Score |
To this:
<Score |
And I also had to give data to a children component through a method exposed in the child component instead of using the properties. This problem comes to serialization/deserialization of the object and causes a re-rendering of the child component where my basic rendering was made by the canvas.
For the next year, I think that I will continue to work with vanillaJS because the use case of the countdown is each time to match with the mindset of a framework. I’m not saying that Vue is a bad framework, but I really think that it wasn’t the best choice for my project.
Code & Demo
You can find the code here: CountDown DevFest 2018.
If you want to see it in action, have a look here CountDown 2018
2019?
I don’t know yet what I will write for 2019 but it will be maybe a new game based on vanilla. Stay tuned 🤘
PS : A huge thanks to Elaine Dias Batista for having taking time to read and correct this article.