Управление производительностью анимаций

Chakra UI предоставляет гибкую систему для работы с анимациями и переходами компонентов. Основой является компонент motion из интеграции с Framer Motion, который позволяет создавать плавные анимации с минимальным количеством кода и без существенного ущерба для производительности.

Использование shouldForwardProp и ограничение перерисовок

При работе с анимациями важно контролировать, какие свойства передаются внутрь DOM. Компоненты Chakra UI позволяют использовать shouldForwardProp, чтобы исключать ненужные пропсы, которые могут вызывать лишние ререндеры:

import { chakra, shouldForwardProp } from "@chakra-ui/react";
import { motion, isValidMotionProp } from "framer-motion";

const MotionBox = chakra(motion.div, {
  shouldForwardProp: (prop) => isValidMotionProp(prop) || shouldForwardProp(prop),
});

Такой подход снижает нагрузку на браузер и предотвращает ненужные обновления.

Аппаратное ускорение через transform

Для плавных анимаций важно использовать CSS-свойства, поддерживаемые GPU, такие как transform и opacity. Chakra UI позволяет напрямую задавать анимации через animate:

<MotionBox
  initial={{ opacity: 0, y: -20 }}
  animate={{ opacity: 1, y: 0 }}
  transition={{ duration: 0.4, ease: "easeOut" }}
/>

Использование transform: translateY вместо top или margin-top позволяет избежать перерисовки layout и снижает нагрузку на главный поток браузера.

Контроль частоты кадров через transition и layout

Chakra UI и Framer Motion поддерживают детальную настройку фреймрейта анимаций:

<MotionBox
  layout
  transition={{ type: "spring", stiffness: 300, damping: 30 }}
/>
  • layout инициирует анимацию изменения размеров или положения элементов.
  • spring с корректными параметрами уменьшает количество кадров, делая анимацию плавной, но менее затратной по производительности.

Использование AnimatePresence для динамических компонентов

Компонент AnimatePresence управляет монтированием и размонтированием элементов с анимацией, предотвращая скачки и лишние перерисовки:

import { AnimatePresence, motion } from "framer-motion";

<AnimatePresence>
  {isVisible && (
    <MotionBox
      key="modal"
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    >
      Контент модального окна
    </MotionBox>
  )}
</AnimatePresence>

AnimatePresence гарантирует, что элемент полностью отрисован до завершения анимации выхода, что снижает визуальные артефакты.

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

Для улучшения производительности и доступности Chakra UI предоставляет хук usePrefersReducedMotion:

import { usePrefersReducedMotion, Box } from "@chakra-ui/react";

const Component = () => {
  const prefersReducedMotion = usePrefersReducedMotion();

  return (
    <Box
      as={motion.div}
      animate={prefersReducedMotion ? {} : { opacity: 1, y: 0 }}
      initial={{ opacity: 0, y: -20 }}
      transition={{ duration: 0.5 }}
    />
  );
};

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

Lazy-rendering и мемоизация компонентов

Для сложных анимаций рекомендуется:

  • Lazy-rendering: создавать элементы только при необходимости через React.lazy или условный рендеринг.
  • Memoization: использовать React.memo для анимируемых компонентов, чтобы перерисовка происходила только при изменении пропсов.
const AnimatedBox = React.memo(({ children, isVisible }) => (
  <MotionBox
    initial={{ opacity: 0 }}
    animate={{ opacity: isVisible ? 1 : 0 }}
    transition={{ duration: 0.3 }}
  >
    {children}
  </MotionBox>
));

Минимизация вложенных анимаций

Слишком глубокая вложенность анимированных компонентов увеличивает нагрузку на рендеринг. Оптимально:

  • Объединять анимации нескольких вложенных элементов в один родительский компонент.
  • Использовать variants для управления группой анимаций:
const containerVariants = {
  hidden: { opacity: 0 },
  visible: { opacity: 1, transition: { staggerChildren: 0.1 } },
};

const childVariants = {
  hidden: { y: 20, opacity: 0 },
  visible: { y: 0, opacity: 1 },
};

<MotionBox variants={containerVariants} initial="hidden" animate="visible">
  <MotionBox variants={childVariants}>Элемент 1</MotionBox>
  <MotionBox variants={childVariants}>Элемент 2</MotionBox>
</MotionBox>

Такой подход снижает количество одновременных анимаций и улучшает FPS.

Настройка will-change и GPU-активация

Chakra UI позволяет напрямую задавать стиль willChange через sx или проп __css:

<MotionBox
  sx={{ willChange: "transform, opacity" }}
  animate={{ transform: "translateY(0)", opacity: 1 }}
  initial={{ transform: "translateY(-20px)", opacity: 0 }}
/>

Предварительное указание браузеру, какие свойства будут анимироваться, позволяет задействовать GPU заранее и уменьшить задержки.

Оптимизация через motion.custom для Chakra-компонентов

Можно создавать обертки motion для всех базовых компонентов Chakra UI (Box, Flex, Button) с целью повторного использования анимационных настроек, что снижает дублирование кода и упрощает управление производительностью:

import { chakra, Box } from "@chakra-ui/react";
import { motion, isValidMotionProp } from "framer-motion";

const MotionButton = chakra(motion(Button), {
  shouldForwardProp: (prop) => isValidMotionProp(prop) || prop === "onClick",
});

Это позволяет стандартизировать анимации и уменьшить количество ререндеров при масштабировании интерфейса.