CSS-анимации через Stimulus

Stimulus — это лёгкий JavaScript-фреймворк, ориентированный на добавление динамики в HTML без необходимости кардинальной перестройки фронтенд-архитектуры. Основная идея заключается в связывании контроллеров с элементами DOM через специальные атрибуты, что позволяет управлять поведением элементов напрямую из JavaScript. При работе с CSS-анимациями этот подход позволяет комбинировать декларативные стили и программное управление их запуском и завершением.

Создание контроллера для анимаций

Контроллер Stimulus — это класс, который наследует Controller из пакета @hotwired/stimulus. В контексте анимаций он может содержать методы для запуска, остановки и перезапуска анимаций на элементах DOM.

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["box"];

  connect() {
    this.boxTarget.classList.add("ready");
  }

  animate() {
    this.boxTarget.classList.add("animate");
    this.boxTarget.addEventListener("animationend", () => {
      this.boxTarget.classList.remove("animate");
    }, { once: true });
  }
}

Ключевые моменты:

  • static targets — определяет элементы внутри контроллера, с которыми будет происходить взаимодействие.
  • Метод connect() вызывается автоматически при подключении контроллера к DOM. Здесь удобно добавлять начальные состояния для анимаций.
  • animationend используется для отслеживания конца CSS-анимации и снятия классов, чтобы анимацию можно было запустить повторно.

Определение CSS-анимаций

CSS-анимации определяются через ключевые кадры (@keyframes) и классы, которые будут применяться динамически из контроллера.

.box {
  width: 100px;
  height: 100px;
  background-color: #3498db;
  transition: transform 0.5s ease, opacity 0.5s ease;
  opacity: 0;
  transform: translateY(20px);
}

.box.ready {
  opacity: 1;
  transform: translateY(0);
}

.box.animate {
  animation: bounce 1s ease forwards;
}

@keyframes bounce {
  0% { transform: translateY(0); }
  50% { transform: translateY(-30px); }
  100% { transform: translateY(0); }
}

Особенности подхода:

  • Класс .ready задаёт базовое состояние элемента после подключения к DOM.
  • Класс .animate запускает анимацию при необходимости.
  • Использование события animationend позволяет синхронизировать JavaScript с завершением CSS-анимации, что критично для повторного воспроизведения и сложных последовательностей.

Работа с несколькими целевыми элементами

Stimulus позволяет определить несколько targets и управлять ими одновременно или по отдельности:

static targets = ["item"];

animateAll() {
  this.itemTargets.forEach(el => {
    el.classList.add("animate");
    el.addEventListener("animationend", () => {
      el.classList.remove("animate");
    }, { once: true });
  });
}

Такой подход особенно полезен для списков и сеток, где анимации должны запускаться поочерёдно или одновременно.

Взаимодействие с событиями пользователя

Stimulus облегчает привязку анимаций к событиям через data-action:

<div data-controller="animation">
  <div data-animation-target="box" class="box"></div>
  <button data-action="click->animation#animate">Запустить анимацию</button>
</div>
  • Атрибут data-action="click->animation#animate" указывает, что при клике вызывается метод animate контроллера animation.
  • Это позволяет управлять анимациями полностью декларативно, без необходимости отдельного написания обработчиков событий.

Последовательные и цепочные анимации

Для создания цепочек анимаций удобно использовать setTimeout или animationend:

animateSequence() {
  this.itemTargets.forEach((el, index) => {
    setTimeout(() => {
      el.classList.add("animate");
      el.addEventListener("animationend", () => {
        el.classList.remove("animate");
      }, { once: true });
    }, index * 200);
  });
}

Такой метод позволяет создавать эффект ступенчатого появления элементов, что часто используется в списках, галереях и интерактивных интерфейсах.

Управление классами и состояниями

Stimulus предоставляет методы для упрощённого манипулирования классами и атрибутами:

toggleAnimation() {
  this.boxTarget.classList.toggle("animate");
}
  • Метод toggle() упрощает повторное включение и отключение анимаций.
  • Комбинация с animationend гарантирует корректное сбрасывание состояния, что предотвращает «залипание» анимации.

Интеграция с другими библиотеками CSS

Stimulus не конфликтует с внешними CSS-библиотеками (например, Animate.css). Контроллер может динамически добавлять классы:

import "animate.css";

animateWithLibrary() {
  this.boxTarget.classList.add("animate__animated", "animate__bounce");
  this.boxTarget.addEventListener("animationend", () => {
    this.boxTarget.classList.remove("animate__animated", "animate__bounce");
  }, { once: true });
}

Это позволяет использовать готовые анимации и комбинировать их с собственными состояниями элементов.

Использование значений для параметров анимации

Stimulus поддерживает values, что делает контроллер более гибким:

static values = {
  duration: Number
};

animate() {
  this.boxTarget.style.animationDuration = `${this.durationValue}s`;
  this.boxTarget.classList.add("animate");
}
  • Значение durationValue можно задать через атрибут data-animation-duration-value="1.5".
  • Это позволяет управлять скоростью анимации без изменения CSS-кода.

Stimulus в связке с CSS-анимациями создаёт мощный инструмент для управления динамикой интерфейсов. Контроллеры обеспечивают декларативное подключение поведения к DOM, а CSS отвечает за визуальные эффекты. Такой подход позволяет строить сложные, отзывчивые и легко поддерживаемые анимации без перегрузки JavaScript.