Bonus Level - vue.js and firebase "Heart Click"
Tags: javascript, html, vue, firebase
Published: 2021-01-30 17:42:21

This is a bonus part for the "Heart Clicker" with vue.js and firebase, you can find it here:
{% link https://dev.to/venatus/tutorial-make-an-heart-clicker-with-vue-js-and-firebase-3npe %}

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 *<style>* tag add define cursor: pointer to our heart, as follow:
```css
<style lang="scss" scoped>
  canvas#heart {
    cursor: pointer;
  }    
</style>
```

![heart with pointer cursor](https://dev-to-uploads.s3.amazonaws.com/i/1qqvof737s7y1rcta7l3.png)

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:
```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:
```html
<canvas
  id="particles"
  :width="particlesWidth"
  :height="particlesHeight"></canvas>
```

And this css:
```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:
```javascript
// 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:
```javascript
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:
```javascript
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:
```javascript
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:
```javascript
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](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)).
- resizeCanvas - a function to adjust the canvas size to the screen.

```javascript
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:
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:
```javascript
this.init(e.clientX, e.clientY);
```

In the component's mounted hook add the following code:
```javascript
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. 🖤