Bonus Level - vue.js and firebase "Heart Click"
Shai Angress
Posted on January 30, 2021
This is a bonus part for the "Heart Clicker" with vue.js and firebase, you can find it here:
Tutorial: Make an Heart "clicker" with vue.js and firebase
Shai Angress ・ Jan 30 '21
In this bonus part we'll add some juice to our heart with a little effort, just by adding particles and some css.
I recommend to take the tutorial, but if you are here just for the particles that's fine too Passepartout ;)
Part 1 - css
Let's start with the css code, it's short and simple.
First let's make it clear for the user that the heart is clickable.
In your tag add define cursor: pointer to our heart, as follow:
<style lang="scss" scoped>
canvas#heart {
cursor: pointer;
}
</style>
Much better.
Now let's add a little animation and resize (optional) our heart, you can play around with the animation to get the nice feeling that you are looking for, I went for a simple width scale using css cubic-bezier timing function.
Add the following code to your css:
canvas#heart {
cursor: pointer;
width: 50px;
transition: width 0.3s;
transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
&:hover {
width: 55px;
}
&:active {
width: 50px;
}
}
Part 2 - Particles!
For the effect of the particles I used a colorful circles that explode from the back of the heart.
First, add a canvas element beneath our heart canvas element and set the id to particles:
<canvas
id="particles"
:width="particlesWidth"
:height="particlesHeight"></canvas>
And this css:
canvas#particles {
position: absolute;
left: 0;
z-index: -1;
}
We'll use a "function class", which is essentially just a function that we instantiate to create an object, a representation of an object if you like.
There are few properties in our class that you can play with and change the behavior of the particles;
- maxLife - how much time will the particle "live".
- radius - size of the circle.
- color - the particle's color.
- randomness of the particle starting point.
Add 3 constants and a function outside of your vue component's definition, inside the script tag:
// a simple util function we'll use later
const random = (min, max) => {
return Math.random() * ( max - min ) + min;
}
const PARTICLES_NUMBER = 150;
const MIN_LIFE = 50;
const MAX_LIFE = 150;
In your vue component methods, add a new method and call it Particle, it takes 4 arguments:
- x - the start X position on the canvas.
- y - the start Y position on the canvas.
- radius - the circle radius.
- index - current index to use as unique id for the particle.
- self - the vue "this"
In the Particle method that you created add the following properties at the top:
this.x = x;
this.y = y;
this.dx = Math.random()*10-5;
this.dy = Math.random()*10-5;
this.gravity = 0;
this.radius = radius;
this.id = index;
this.life = 0;
this.maxLife = random(MIN_LIFE, MAX_LIFE);
this.color = self.colors[Math.floor(Math.random()*self.colors.length)];
To draw the circle in our Particle class we'll add a draw function which will be part of the Particle instance, that's why we add it to the Particle's this, we'll use this function later in our update function.
Add the draw function beneath the properties inside the Particle:
this.draw = () => {
self.ctx.beginPath();
self.ctx.arc(this.x,this.y,this.radius,0,Math.PI*2, false);
self.ctx.strokeStyle = this.color;
self.ctx.lineWidth = 4;
self.ctx.stroke();
}
That's the drawing part, now we want to animate the particle so we'll add an update function to our Particle:
this.update = () => {
if (this.x + this.radius > self.particlesWidth || this.x - this.radius < 0){
this.dx = -this.dx;
}
if (this.y + this.radius > self.particlesHeight || this.y - this.radius < 0){
this.dy = -this.dy;
}
this.life++;
if (this.life >= this.maxLife) {
delete self.particles[this.id];
}
this.x+=this.dx;
this.y+=this.dy;
this.dy += this.gravity;
this.draw();
}
The update function moves the particle each time it's called according to its current position and checks if it passed the maximum life time, if so, it deletes the particle from the particles array that we'll add to our component.
Part 3 - draw particles on component's canvas
Now we want to use the Particle class that we created in our vue component.
Add the following properties to the component's data:
particlesWidth: 1000,
particlesHeight: 1000,
particles: [],
ctx: null,
colors: ['rgba(243,82,92,0.8)','rgba(0,103,76,0.5)','rgba(149,178,58,0.5)','rgba(252,206,68,0.8)','rgba(245,127,79,0.5)']
We'll add 3 new methods:
- init - will be called every time we want to make an explosion.
- animateParticle - animate the particle each frame (see requestAnimationFrame).
- resizeCanvas - a function to adjust the canvas size to the screen.
init(x, y) {
this.particles = (new Array(PARTICLES_NUMBER)).fill(null)
.map((v, i) => new this.Particle(x, y, 0.5, i, this));
},
animateParticle() {
requestAnimationFrame(this.animateParticle);
// COOLNESS: if we don't clear rect it becomes a cool random drawing tool!
this.ctx.clearRect(0, 0, this.particlesWidth, this.particlesHeight);
this.particles.forEach(part => part.update());
},
resizeCanvas(canvas) {
canvas.width = this.particlesWidth = window.innerWidth;
canvas.height = this.particlesHeight = window.innerHeight;
}
2 things left:
- Call the init function when user clicks the heart.
- Activate the animation for the particles.
Add the following line to our onClick method:
this.init(e.clientX, e.clientY);
In the component's mounted hook add the following code:
const particlesCanvas = this.$el.querySelector('#particles');
this.resizeCanvas(particlesCanvas);
window.addEventListener('resize', () => this.resizeCanvas(particlesCanvas));
if (particlesCanvas.getContext) {
this.ctx = particlesCanvas.getContext('2d');
this.animateParticle();
}
Congratulations! you finished the bonus level! kudos to you!
Summary
You've learned on this tutorial how to add some juice to your work using javascript and little css, I urge you to play around with the effect, go crazy, and share your results ;).
Thanks for taking this tutorial, I hope you enjoyed and learned something new and practical. 🖤
Posted on January 30, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.