Анимации и переходы

Lit — современный фреймворк для создания веб-компонентов на JavaScript, обеспечивающий декларативное обновление DOM с высокой производительностью. Работа с анимациями и переходами в Lit строится на сочетании стандартных CSS-технологий и встроенных возможностей жизненного цикла компонентов. Понимание этих механизмов позволяет создавать плавные и отзывчивые интерфейсы.


Основы работы с анимациями

Анимации в Lit чаще всего реализуются через CSS-транзишены и CSS-анимации, применяемые к шаблонам компонентов. Основное преимущество Lit заключается в том, что изменения DOM автоматически реагируют на обновления свойств, что позволяет синхронизировать анимации с состоянием компонента.

Пример использования CSS-перехода:

import { LitElement, html, css } from 'lit';

class FadeBox extends LitElement {
  static properties = {
    visible: { type: Boolean }
  };

  static styles = css`
    div {
      width: 100px;
      height: 100px;
      background-color: blue;
      transition: opacity 0.5s ease-in-out;
      opacity: 0;
    }
    div.visible {
      opacity: 1;
    }
  `;

  render() {
    return html`
      
`; } } customElements.define('fade-box', FadeBox);

В этом примере добавление или удаление класса visible плавно изменяет прозрачность элемента. Lit автоматически обновляет DOM при изменении свойства visible.


Жизненный цикл и анимации

Lit предоставляет методы жизненного цикла компонента, которые полезны для реализации анимаций:

  • firstUpdated(changedProperties) — вызывается после первого рендера компонента. Идеально подходит для запуска анимаций при монтировании элемента.
  • updated(changedProperties) — вызывается после каждого обновления свойства. Позволяет инициировать анимацию при изменении состояния.
  • connectedCallback() и disconnectedCallback() — можно использовать для инициализации и очистки таймеров анимаций.

Пример запуска анимации после рендера:

updated(changedProperties) {
  if (changedProperties.has('visible') && this.visible) {
    const box = this.shadowRoot.querySelector('div');
    box.animate([{ transform: 'scale(0)' }, { transform: 'scale(1)' }], {
      duration: 500,
      easing: 'ease-out'
    });
  }
}

Метод animate использует Web Animations API, позволяя создавать сложные анимации без дополнительных библиотек.


Анимации при добавлении и удалении элементов

Для динамических списков или условного рендера важны переходы появления и исчезновения элементов. В Lit это реализуется через сочетание условного рендера и CSS-анимаций:

render() {
  return html`
    ${this.items.map(
      item => html`
        
${item}
` )} `; } static styles = css` @keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } .item { margin: 5px 0; } `;

Использование CSS-анимации @keyframes позволяет плавно визуализировать появление элементов в списке.


Комбинирование анимаций и реактивных свойств

Lit позволяет связывать состояние компонента с анимациями, создавая интерактивные интерфейсы. Например, анимация можно запускать в зависимости от свойства:

render() {
  return html`
    
    
`; } static styles = css` .box { width: 100px; height: 100px; background: red; transition: all 0.5s ease; } .box.open { transform: translateX(100px) rotate(20deg); background: green; } .box.closed { transform: translateX(0) rotate(0); background: red; } `;

Изменение свойства toggle автоматически обновляет DOM и инициирует переход.


Переходы между состояниями через requestUpdate

Иногда необходимо контролировать порядок анимаций или отложенные обновления. Метод requestUpdate позволяет вручную инициировать реактивное обновление, что полезно для цепочек анимаций:

async triggerAnimation() {
  this.state = 'start';
  await this.updateComplete;
  this.state = 'end';
}

updateComplete — промис, который завершается после следующего рендера компонента, что обеспечивает синхронизацию анимации с DOM.


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

Для сложных анимаций важно учитывать:

  • Использование transform и opacity вместо top/left — эти свойства анимируются GPU и не вызывают пересчет макета.
  • Минимизация числа наблюдаемых свойств — каждая реактивная переменная вызывает ререндер компонента.
  • Тонкая настройка CSS-транзишнов — использование transition: all может снижать производительность.

Lit позволяет комбинировать эти подходы с реактивностью компонентов, создавая плавные и отзывчивые интерфейсы даже при большом числе элементов.