Nuxt, Meet Particles
Tags: nuxt, vue, javascript, particlesjs
Published: 2020-04-14 20:48:16

This article is part of a series on my experiences with Nuxt.js that I built into the [nuxt-toolkit](https://github.com/overscore-media/nuxt-toolkit) by [OverScore Media](https://overscore.media) {% github https://github.com/overscore-media/nuxt-toolkit no-readme %}

See a live example at https://nuxt-toolkit.overscore.media! :]

-----

Ah, particle effects. Wondrous things, really. And I can think of no better particle effects library for the web (that isn't something related to game development or 3D rendering) than Particles.JS.

{% github https://github.com/VincentGarreau/particles.js %}

Now, how does one go about implementing this awesome library into one's Nuxt project? Sure, there _are_ at least a few Vue libraries that work with Particles, but I was able to hack together a complete component without using any other libraries.

## Here's the Code

In its entirety, this is my implementation, inspired by https://www.npmjs.com/package/vue-particles-generator and https://github.com/creotip/vue-particles.

```vue
<template>
  <div
    :id="id"
    class="particles-js"
    :color="color"
    :particleOpacity="particleOpacity"
    :linesColor="linesColor"
    :particlesNumber="particlesNumber"
    :shapeType="shapeType"
    :particleSize="particleSize"
    :linesWidth="linesWidth"
    :lineLinked="lineLinked"
    :lineOpacity="lineOpacity"
    :linesDistance="linesDistance"
    :moveSpeed="moveSpeed"
    :movementDirection="movementDirection"
    :hoverEffect="hoverEffect"
    :hoverMode="hoverMode"
    :clickEffect="clickEffect"
    :clickMode="clickMode"
  ></div>
</template>
<script>
/* eslint-disable */
export default {
  props: {
    color: {
      type: String,
      default: '#dedede'
    },
    particleOpacity: {
      type: Number,
      default: 1.0
    },
    particlesNumber: {
      type: Number,
      default: 80
    },
    shapeType: {
      type: String,
      default: 'circle'
    },
    particleSize: {
      type: Number,
      default: 4
    },
    linesColor: {
      type: String,
      default: '#dedede'
    },
    linesWidth: {
      type: Number,
      default: 1
    },
    lineLinked: {
      type: Boolean,
      default: true
    },
    lineOpacity: {
      type: Number,
      default: 0.4
    },
    linesDistance: {
      type: Number,
      default: 150
    },
    moveSpeed: {
      type: Number,
      default: 3
    },
    movementDirection: {
      type: String,
      default: 'bottom-left'
    },
    hoverEffect: {
      type: Boolean,
      default: false
    },
    hoverMode: {
      type: String,
      default: 'grab'
    },
    clickEffect: {
      type: Boolean,
      default: false
    },
    clickMode: {
      type: String,
      default: 'push'
    }
  },
  data () {
    return {
      id: 'particles-instance-' + Math.floor(Math.random() * 5000)
    }
  },
  mounted () {
    require('particles.js')
    this.$nextTick(() => {
      this.initParticleJS(
        this.color,
        this.particleOpacity,
        this.particlesNumber,
        this.shapeType,
        this.particleSize,
        this.linesColor,
        this.linesWidth,
        this.lineLinked,
        this.lineOpacity,
        this.linesDistance,
        this.moveSpeed,
        this.movementDirection,
        this.hoverEffect,
        this.hoverMode,
        this.clickEffect,
        this.clickMode
      )
    })
  },
  methods: {
    initParticleJS (
      color,
      particleOpacity,
      particlesNumber,
      shapeType,
      particleSize,
      linesColor,
      linesWidth,
      lineLinked,
      lineOpacity,
      linesDistance,
      moveSpeed,
      movementDirection,
      hoverEffect,
      hoverMode,
      clickEffect,
      clickMode
    ) {
        particlesJS(this.id, {
          "particles": {
            "number": {
              "value": particlesNumber,
              "density": {
                "enable": true,
                "value_area": 800
              }
            },
            "color": {
              "value": color
            },
            "shape": {
            // circle, edge, triangle, polygon, star, image
              "type": shapeType,
              "stroke": {
                "width": 0,
                "color": "#192231"
              },
              "polygon": {
                "nb_sides": 5
              }
            },
            "opacity": {
              "value": particleOpacity,
              "random": false,
              "anim": {
                "enable": false,
                "speed": 1,
                "opacity_min": 0.1,
                "sync": false
              }
            },
            "size": {
              "value": particleSize,
              "random": true,
              "anim": {
                "enable": false,
                "speed": 40,
                "size_min": 0.1,
                "sync": false
              }
            },
            "line_linked": {
              "enable": lineLinked,
              "distance": linesDistance,
              "color": linesColor,
              "opacity": lineOpacity,
              "width": linesWidth
            },
            "move": {
              "enable": true,
              "speed": moveSpeed,
              "direction": movementDirection,
              "random": false,
              "straight": false,
              "out_mode": "out",
              "bounce": false,
              "attract": {
                "enable": false,
                "rotateX": 600,
                "rotateY": 1200
              }
            }
          },
          "interactivity": {
            "detect_on": "canvas",
            "events": {
              "onhover": {
                "enable": hoverEffect,
                "mode": hoverMode
              },
              "onclick": {
                "enable": clickEffect,
                "mode": clickMode
              },
              "onresize": {
                "enable": true,
                "density_auto": true,
                "density_area": 400
              }
            },
            "modes": {
              "grab": {
                "distance": 140,
                "line_linked": {
                  "opacity": 1
                }
              },
              "bubble": {
                "distance": 400,
                "size": 40,
                "duration": 2,
                "opacity": 8,
                "speed": 3
              },
              "repulse": {
                "distance": 200,
                "duration": 0.4
              },
              "push": {
                "particles_nb": 4
              },
              "remove": {
                "particles_nb": 2
              }
            }
          },
          "retina_detect": true
        })
    }
  }
}
</script>
```

Well, that was a doozy! Anyway, to use it, do something like this (don't miss the `<client-only></client-only>`:

```vue
<template>
<!-- -->
  <client-only>
    <Particles
      color="#DCBA8F"
      :particles-number="100"
      shape-type="star"
      :particle-size="3"
      movement-direction="top"
      lines-color="#dedede"
      :line-linked="false"
      :move-speed="0.75"
    />
  </client-only>
<!-- -->
</template>

<script>
import Particles from '~/components/Particles.vue'

export default {
  components: {
    Particles
  }
}
</script>

<style lang="scss">
div[id^="particles-instance-"] {
  height: 100vh !important;
  width: 100vw !important;
  position: fixed !important;
  top: 0 !important;
  margin: 0 !important;
  padding: 0 !important;
  overflow: hidden !important;
  background: rgba($color: #05114e, $alpha: 0.4);
  z-index: 2 !important;
}
</style>
```

The above code gives you a fullscreen particles background. Mixed with something like this (with a background of `~assets/img/background.png`):

```scss
.hero-background {
  height: 100vh !important;
  width: 100vw !important;
  position: fixed !important;
  top: 0 !important;
  margin: 0 !important;
  padding: 0 !important;
  overflow: hidden !important;
  background-size: cover !important;
  background-repeat: no-repeat !important;
  background-blend-mode: overlay !important;
  background-attachment: fixed !important;
  background-position: fixed !important;
  background: rgba($color: #05114e, $alpha: 0.4);
  background-image: url("~assets/img/background.png") !important;
  z-index: 1 !important;
}
```

you'd get a nice overlay. There are a ton of options, really; it's all CSS past this point. https://vincentgarreau.com/particles.js/ is your friend when it comes to checking out what changing props does.

Hope this was helpful; enjoy! Stay safe and keep on coding!