<template lang="pug">
  #challenge
    button.btn(@click="buttonClick") Click this Button
      .icon(
        v-for="icon, i in icons"
        v-if="icon != null"
        v-bind:class="{" +
                     "'icon-blue' : icon.color === 1," +
                     "'icon-green' : icon.color === 2," +
                     "'icon-red' : icon.color === 3" +
                     "}"
        v-bind:style="{" +
                     "'top' : icon.from.y + 'px'," + 
                     "'left' : icon.from.x + 'px'" +
                     "}"
        v-bind:ref="'icon-' + i"
      )
        i.fas(
          v-bind:class="{" +
                       "'fa-star' : icon.shape === 1," +
                       "'fa-square' : icon.shape === 2," +
                       "'fa-circle' : icon.shape === 3," +
                       "'fa-play' : icon.shape === 4," +
                       "'fa-heart' : icon.shape === 5," +
                       "'fa-certificate' : icon.shape === 6," +
                       "'fa-cloud' : icon.shape === 7," +
                       "'fa-folder' : icon.shape === 8" +
                       "}"
           v-bind:style="{" +
                        "'font-size' : icon.size + 'px'" +
                        "}"
        )
</template>

<script>
  export default {
    data() {
      return {
        angle: {
          min: 0,
          max: 360
        },
        distance: {
          min: 60,
          max: 100
        },
        size: {
          min: 10,
          max: 35
        },
        count: {
          min: 4,
          max: 12
        },
        color: {
          min: 1,
          max: 3
        },
        // These shapes are based on icon name in Fontawesome
        shape: {
          min: 1,
          max: 8
        },
        speed: {
          min: 1200,
          max: 2400
        },
        spin: {
          min: 1,
          max: 3
        },
        icons: []
      };
    },
    methods: {
      // Generates random number.
      random(min, max) {
        return Math.round(Math.random() * (max - min)) + min;
      },
      // Event for button click
      buttonClick(evt) {
        for (let i = 0; i < this.random(this.count.min, this.count.max); i++) {
          let a = this.random(this.angle.min, this.angle.max);
          let d = this.random(this.distance.min, this.distance.max);
          let s = this.random(this.size.min, this.size.max);
          let x = evt.offsetX - (s / 2);
          let y = evt.offsetY - (s / 2);

          this.icons.push({
            from: {
              x: x,
              y: y
            },
            to: {
              x: x + Math.cos((a * Math.PI) / 180) * d,
              y: y + Math.sin((a * Math.PI) / 180) * d
            },
            color: this.random(this.color.min, this.color.max),
            shape: this.random(this.shape.min, this.shape.max),
            size: s,
            speed: this.random(this.speed.min, this.speed.max),
            spin: this.random(this.spin.min, this.spin.max)
          });
        }
      }
    },
    updated() {
      let self = this;
      
      for (let i = 0; i < self.icons.length; i++) {
        let icon = this.icons[i];
        
        if (icon === null) {
          continue;
        }
        
        let elm = self.$refs["icon-" + i];
        let time = icon.speed / 1000;
        let tl = gsap.timeline();
        
        tl.to(elm, {
          top: icon.to.y + "px",
          left: icon.to.x + "px",
          rotate: (icon.spin * 360) + "deg",
          duration: time,
          ease: "power2.out"
        });
        
        let delay = time / 3;
        
        tl.to(elm, {
          opacity: 0,
          duration: time - delay,
          delay: delay,
          ease: "power2.out",
          onComplete() {
            self.icons[i] = null;
            
            self.$forceUpdate();
          }
        }, "-=" + time);
      }
    }
  };
</script>

<style lang="scss">
  $color-1: #2374c6;
  $color-2: #c20f00;
  $color-3: #ffdd22;
  $color-4: #ffffff;
  $color-5: #000000;
  
  @import url("https://fonts.googleapis.com/css?family=Montserrat:400,400i,700");
  
  #challenge {
    font-family: Montserrat, sans-serif;
    width: 100vw;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
  }
  
  .btn {
    color: $color-5;
    font-family: inherit;
    font-size: 24px;
    font-weight: bold;
    background-color: $color-4;
    width: 220px;
    height: 90px;
    padding: 0 20px;
    border: solid 4px $color-5;
    margin: 0;
    outline: 0;
    flex-shrink: 0;
    box-sizing: border-box;
    position: relative;
    user-select: none;
    border-radius: 12px;
    box-shadow: 4px 4px $color-5;
    transform: translate(-2px, -2px);
    transition: transform 40ms ease-out, box-shadow 40ms ease-out;
    &:after {
      content: '';
      width: 100%;
      height: 100%;
      position: absolute;
      top: 0;
      left: 0;
    }
    &:active {
      box-shadow: 0px 0px $color-5;
      transform: translate(0px, 0px);
    }
  }
  
  .icon {
    position: absolute;
    &.icon-blue {
      color: $color-1;
    }
    &.icon-green {
      color: $color-2;
    }
    &.icon-red {
      color: $color-3;
    }
  }
</style>

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/gsap/3.5.0/gsap.min.js