Задержки для плавных переходов

В React Aria управление задержками анимации и переходов реализуется через комбинацию хуков и утилит для создания плавного, доступного пользовательского интерфейса. Основной инструмент для работы с задержками — это хук useDelayGroup и связанные с ним механизмы контроля последовательности анимаций элементов.


Хук useDelayGroup

useDelayGroup позволяет координировать анимации группы элементов, задавая стартовую задержку для каждого элемента и общую задержку группы. Его использование особенно полезно в списках, меню, всплывающих панелях и при открытии модальных окон, где требуется постепенное появление элементов.

Пример базовой структуры:

import { useDelayGroup } from '@react-aria/overlays';

function AnimatedList({ items }) {
  let { delay, isEntering } = useDelayGroup();
  
  return (
    <ul>
      {items.map((item, index) => (
        <li
          key={item.id}
          style={{
            transitionDelay: `${index * delay}ms`,
            opacity: isEntering ? 1 : 0,
            transform: isEntering ? 'translateY(0)' : 'translateY(10px)',
            transition: 'opacity 200ms, transform 200ms'
          }}
        >
          {item.label}
        </li>
      ))}
    </ul>
  );
}

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

  • delay определяет промежуток между запуском анимаций соседних элементов.
  • isEntering указывает, активна ли анимация появления.
  • Использование CSS-свойств opacity и transform обеспечивает плавное визуальное восприятие.

Комбинирование с useTransitionState

Для сложных переходов React Aria предлагает хук useTransitionState, который управляет состояниями входа и выхода элемента. Он обеспечивает контроль над моментами entering, entered, exiting и exited.

Пример:

import { useTransitionState } from '@react-aria/overlays';

function FadeInPanel({ isOpen, children }) {
  let state = useTransitionState({ visible: isOpen });

  return (
    <div
      style={{
        opacity: state.isEntering || state.isEntered ? 1 : 0,
        transform: state.isEntering || state.isEntered ? 'translateY(0)' : 'translateY(-10px)',
        transition: 'opacity 250ms ease, transform 250ms ease'
      }}
    >
      {children}
    </div>
  );
}

Особенности:

  • Состояние isEntering активирует анимацию появления.
  • Состояние isExiting позволяет плавно скрывать элемент.
  • Синхронизация с useDelayGroup позволяет создавать последовательные эффекты для нескольких элементов.

Настройка задержек для последовательного появления

Появление элементов поочередно делает интерфейс более естественным и читаемым. Для этого используют параметр stagger — интервал между запуском анимаций соседних компонентов.

Пример с задержкой:

import { useDelayGroup } from '@react-aria/overlays';

function Menu({ items }) {
  let { delay, isEntering } = useDelayGroup();

  return (
    <nav>
      {items.map((item, index) => (
        <a
          key={item.id}
          href={item.href}
          style={{
            transitionDelay: `${index * 50}ms`, // 50 мс между элементами
            opacity: isEntering ? 1 : 0,
            transform: isEntering ? 'translateX(0)' : 'translateX(-20px)',
            transition: 'opacity 300ms ease, transform 300ms ease'
          }}
        >
          {item.label}
        </a>
      ))}
    </nav>
  );
}

Динамическое управление задержками

React Aria позволяет изменять задержки в зависимости от условий: скорости сети, взаимодействия пользователя или контекста. Для этого используют функцию getDelay:

function getDelay(index, baseDelay = 100) {
  return index * baseDelay;
}

items.map((item, index) => ({
  style: {
    transitionDelay: `${getDelay(index)}ms`
  }
}));

Такой подход делает интерфейс гибким и адаптивным.


Синхронизация анимаций с событиями пользователя

Иногда требуется запускать задержку после конкретного события, например, клика или фокуса. Для этого комбинируют useDelayGroup с обработчиками событий:

function Dropdown({ options }) {
  let { delay, isEntering, setEntering } = useDelayGroup();

  return (
    <ul
      onMouseEn ter={() => setEntering(true)}
      onMouseLe ave={() => setEntering(false)}
    >
      {options.map((opt, idx) => (
        <li
          key={opt.id}
          style={{
            transitionDelay: `${idx * delay}ms`,
            opacity: isEntering ? 1 : 0,
            transform: isEntering ? 'translateY(0)' : 'translateY(5px)',
            transition: 'opacity 200ms, transform 200ms'
          }}
        >
          {opt.label}
        </li>
      ))}
    </ul>
  );
}

Это обеспечивает интерактивные, отзывчивые переходы, не нарушающие доступность интерфейса.


Практические рекомендации

  • Использовать opacity и transform вместо display или visibility для плавных переходов.
  • Минимизировать продолжительность анимаций (100–300 мс) для быстрого восприятия.
  • Комбинировать useDelayGroup и useTransitionState для комплексных эффектов.
  • Для больших списков использовать динамическую генерацию задержек, чтобы избежать перегрузки рендеринга.
  • Всегда проверять доступность: задержки не должны мешать клавиатурной навигации или скринридерам.

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

Поскольку множественные анимации могут нагружать браузер, рекомендуется:

  • Использовать GPU-ускоренные свойства (transform, opacity) вместо изменения top, left или height.
  • Ограничивать количество одновременно анимируемых элементов.
  • При необходимости отключать анимацию на слабых устройствах, используя условия на window.matchMedia('(prefers-reduced-motion: reduce)').

Эти практики позволяют создавать плавные, отзывчивые и доступные интерфейсы с помощью React Aria, полностью контролируя задержки и последовательность анимаций.