JSFiddle - React, Tailwind, and code Playground

by Sergey khorev

HTML

<div class="falling-words">
  <span class="word">Inspiration</span>
  <span class="word">Creativity</span>
  <span class="word">Innovation</span>
  <span class="word">Success</span>
</div>

CSS

body {
  margin: 0;
  padding: 0;
  background-color: black;
  color: white;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  overflow: hidden;
  position: relative;
}

.falling-words {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 48px;
  text-align: center;
}

.word {
  position: absolute;
  opacity: 0;
  animation: explode 1s ease-out forwards, rotate 1.5s linear forwards;
  transform-origin: center;
  color: hsl(0, 100%, 50%);
}

@keyframes explode {
  0% {
    opacity: 1;
    transform: scale(0.5) translate(0, 0);
    filter: blur(0);
  }
  70% {
    opacity: 1;
    transform: scale(1) translate(calc(300px * var(--dir-x)), calc(300px * var(--dir-y)));
    filter: blur(2px);
  }
  100% {
    opacity: 0;
    transform: scale(1.5) translate(calc(500px * var(--dir-x)), calc(500px * var(--dir-y)));
    filter: blur(5px);
  }
}

@keyframes rotate {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(calc(360deg * var(--rotate-dir)));
  }
}

JavaScript

const words = document.querySelectorAll('.word');

words.forEach((word, index) => {
  // Устанавливаем случайные направления движения
  const dirX = (Math.random() * 2 - 1).toFixed(2); // случайное значение от -1 до 1
  const dirY = (Math.random() * 2 - 1).toFixed(2); // случайное значение от -1 до 1
  const rotateDir = Math.random() < 0.5 ? -1 : 1;

  word.style.setProperty('--dir-x', dirX);
  word.style.setProperty('--dir-y', dirY);
  word.style.setProperty('--rotate-dir', rotateDir);

  // Задержка перед запуском анимации
  word.style.animationDelay = `${index * 0.3}s`;

  // Установка случайного цвета для каждого слова
  const hue = Math.floor(Math.random() * 360);
  word.style.color = `hsl(${hue}, 100%, 50%)`;

  // Перезапуск анимации после завершения
  word.addEventListener('animationend', () => {
    setTimeout(() => {
      word.style.animation = 'none';
      word.offsetHeight; // триггер для перерасчета
      word.style.animation = ''; // сброс анимации
    }, 1000); // задержка перед повтором анимации
  });
});