DEV Community

Cover image for Bonus Level - vue.js and firebase "Heart Click"
Shai Angress
Shai Angress

Posted on • Edited on

Bonus Level - vue.js and firebase "Heart Click"

This is a bonus part for the "Heart Clicker" with vue.js and firebase, you can find it here:

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>
Enter fullscreen mode Exit fullscreen mode

heart with pointer cursor

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

And this css:

canvas#particles {
  position: absolute;
  left: 0;
  z-index: -1;
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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)];
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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();

}
Enter fullscreen mode Exit fullscreen mode

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)']
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

2 things left:

  1. Call the init function when user clicks the heart.
  2. Activate the animation for the particles.

Add the following line to our onClick method:

this.init(e.clientX, e.clientY);
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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. 🖤

Top comments (0)