Absorbing Particles Animation

Hi everyone!
Here is another fun animation I created to try out some things:

The full code looks as follows:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Animating Dynamic Elements</title>

  <style>
    body {
      margin: 0;
      padding: 0;

      width: 100vw;
      height: 100vh;

      display: grid;
      place-items: center;
    }

    .container {
      width: 400px;
      height: 400px;
      display: grid;
      place-items: center;
      overflow: hidden;
      background-color: #FFF;

      animation-name: pulse;
      animation-timing-function: cubic-bezier(1,-0.42,.19,1.37);
      animation-duration: 2s;
      animation-iteration-count: infinite;
      animation-direction:alternate-reverse;
    }

    @keyframes pulse {
      0% {
        filter: drop-shadow(0px 0px 5px #02ffee);
      }
      50% {
        filter: drop-shadow(0px 0px 20px #f6d603);
      }
      100% {
        filter: drop-shadow(0px 0px 40px #fb00ff52);
      }
    }

    .main {
      display: grid;
      place-items: center;
      position: relative;
    }

    .particle {
      width: 50px;
      height: 50px;

      background-color: #76bfff;
      border-radius: 50%;
      opacity: 0;
      position: absolute;
      will-change: transform;

      animation-name: absorb;
      animation-timing-function: cubic-bezier(1, -0.01, .3, 1.32);
      animation-iteration-count: infinite;
      animation-fill-mode: backwards;
    }

    @keyframes absorb {
      0% {
        scale: 0;
        opacity: 0;
      }

      100% {
        translate: -50% -50%;
        scale: var(--scale);
        opacity: var(--opacity);
      }
    }

    .robot {
      animation-name: jittery;
      animation-duration: .5s;
      animation-iteration-count: infinite;

      width: 100px;
      position: absolute;
      z-index: 100;

      filter: drop-shadow(0px 2px 10px #666);
    }

    @keyframes jittery {
      10% {
        transform: translate(-2px, -3px) scale(1.01, 1.01);
      }

      20% {
        transform: translate(3px, 2px) scale(.99, .99);
      }

      30% {
        transform: translate(-4px, -5px) scale(1.01, 1.01);
      }

      40% {
        transform: translate(2px, 3px) scale(1, 1);
      }

      50% {
        transform: translate(-1px, -2px) scale(.98, .98);
      }

      60% {
        transform: translate(0px, 3px) scale(1.02, 1.02);
      }

      70% {
        transform: translate(-2px, -4px) scale(1, 1);
      }

      80% {
        transform: translate(3px, 5px) scale(.99, .99);
      }

      90% {
        transform: translate(-5px, -3px) scale(1.1, 1.1);
      }

      100% {
        transform: translate(3px, 1px) scale(.95, .95);
      }
    }
  </style>
</head>

<body>
  <div class="container">
    <div class="main">
      <img class="robot" src="https://www.kirupa.com/icon/1f916.svg">
    </div>
  </div>

  <template id="particlesTemplate">
    <div class="particleContainer">
      <div class="particle"></div>
    </div>
  </template>

  <script>
    

    let main = document.querySelector(".main");
    let container = document.querySelector(".container");
    let width = container.offsetWidth;
    let height = container.offsetHeight;

    let numberOfparticles = 20;

    let elements = [];

    function drawElements() {
      let template = document.querySelector("#particlesTemplate").content;

      for (let i = 0; i < 200; i++) {

        let particle = template.querySelector(".particle");
        let particleWrapper = template.querySelector(".particleWrapper");

        setItemProperties(particle, true);

        let newElement = main.appendChild(document.importNode(template, true));
      }
    }

    drawElements();

      function setItemProperties(item, initial) {
        let duration = 2 + Math.random() * 4;

        item.style.opacity = 0;
        item.style.scale = 0;

        let randomX = -1 * width + Math.random() * 2 * width;
        let randomY = -1 * height + Math.random() * 2 * height;

        item.style.setProperty("--scale", .1 + Math.random() * 1);
        item.style.setProperty("--opacity", .1 + Math.random() * 1);

        item.style.translate = `${Math.round(randomX)}px ${Math.round(randomY)}px`;

        if (initial) {
          item.style.animationDelay = -1 * (Math.random() * duration) + "s";
        }

        item.style.animationDuration = duration + "s";
      }

      function resetElement(event) {
        if (event.target.className == "particle") {
          setItemProperties(event.target, false);
        }
      }

      window.addEventListener("animationiteration", resetElement, false);
  </script>
</body>

</html>

I’ll explain more on how I created this in the future, but the above is the full code in case you want to try it out yourself! :slight_smile:

You can also read my Twitter summary here: https://twitter.com/kirupa/status/1698528371414900823

Cheers,
Kirupa