Framer Motion: создание анимаций в React

Анимация — ключевая часть почти любого современного веб‑приложения. Однако её трудно сделать правильно: нужно учитывать перформанс, доступность, поведение при разных состояниях и интеграцию со стейтом. Framer Motion — библиотека для React, которая значительно упрощает эти задачи и делает код чище и предсказуемее.
Как работает Framer Motion
Framer Motion использует специальные motion‑компоненты для анимации. Каждому HTML/SVG‑элементу соответствует эквивалентный motion‑элемент с пропсами для жестов и анимаций. Например, обычный HTML‑div выглядит так:
А эквивалент в Framer Motion — так:
Motion‑компоненты поддерживают проп animate, который запускает анимацию при изменении значений. Для более сложных сценариев используют хук useAnimate и локальные ref.
Ключевые термины в одной строке:
- motion‑компонент — компонент, который рендерит HTML/SVG и принимает анимационные пропсы.
- variants — набор предопределённых состояний анимации.
- AnimatePresence — компонент, разрешающий exit‑анимации перед удалением из DOM.
Требования и подготовка
Перед началом убедитесь, что на машине установлены Node.js и менеджер пакетов (npm или Yarn), и вы понимаете основы React. Проект в примере доступен на GitHub. Используйте ветку starter‑files как шаблон, а final‑files — для окончательного демо. Есть также live‑demo проекта.
Откройте терминал и выполните:
git clone git@github.com:makeuseofcode/framer-motion-app.git
cd framer-motion-app
yarn
yarn dev
Открыв localhost:5173 в браузере, вы увидите стартовый экран:

Простая анимация: кнопка с эффектом hover
Создадим первую анимацию: кнопка, которая увеличивается при наведении и возвращается в исходное состояние при уходе курсора.
Откройте файл src/App.jsx. В нём есть функциональный компонент, который импортирует и рендерит компонент Button, передавая проп text:
Animated Button
Move your mouse over the button to see the effect
В компоненте Button импортируйте motion из framer‑motion:
import { motion } from "framer-motion"
Замените обычный button на motion.button и добавьте проп whileHover с желаемыми значениями:
{text}
Теперь кнопка анимируется при наведении курсора.
Важно: Framer Motion удобен тем, что идеально интегрируется со стейтом React — вы можете анимировать значения, основанные на пропсах и локальном состоянии, и при этом код остаётся декларативным.
Анимация модального окна: Backdrop и Modal
Давайте разберём более сложный пример — модальное окно с backdrop, включающее входную и выходную анимации.
В Backdrop.jsx импортируйте motion и создайте компонент с пропами onClick и children. Верните motion.div с классом backdrop и слушателем onClick:
export default function Backdrop() {
return (<>
>)
}
Добавим три ключевых пропа: initial, animate и exit — это исходное состояние, целевое состояние и состояние при выходе соответственно. Также установим transition.duration = 0.34 с:
export default function Backdrop ({onClick, children}){
return (<>
{children}
>)
}
Backdrop служит обёрткой для Modal: клик по backdrop закрывает модалку. В Modal.jsx импортируйте motion и Backdrop. Компонент принимает closeModal и text.
Создайте объект variants с предопределёнными состояниями:
const variants = {
initial: {
y: "-200%",
opacity: 1,
},
visible: {
y: "50%",
transition: {
duration: 0.1,
type: "spring",
damping: 32,
stiffness: 500
}
},
exit: {
y: "200%",
}
}
Верните motion.div, обёрнутый Backdrop, с пропом variants:
e.stopPropagation()}
className="modal"
variants={variants}
initial='initial'
animate='visible'
exit='exit'
>
{text}
Variants — это удобный способ описать все состояния компонента декларативно.
Показать/скрыть модалку и AnimatePresence
В App.jsx импортируйте useState и Modal:
import { useState } from "react";
import Modal from "./components/Modal";
Создайте состояние modalOpen и функцию closeModal:
const [modalOpen, setModalOpen] = useState(false);
function closeModal() {
setModalOpen(false)
}
Вставьте условный рендер модалки:
{modalOpen &&
Если вы заметите, то при простом условном рендере React удаляет компонент из DOM без exit‑анимации. Чтобы разрешить анимации выхода, используйте AnimatePresence. Импортируйте его:
import {AnimatePresence} from 'framer-motion';
Обёрните Modal в AnimatePresence и установите initial={false} и mode=”wait”:
{modalOpen && }
AnimatePresence даёт Framer Motion время завершить exit‑анимацию перед удалением элемента из DOM.
Анимация элементов при прокрутке
Framer Motion умеет запускать анимацию, когда элемент появляется в области просмотра, с помощью пропа whileInView. В ScrollElement.jsx импортируйте motion и замените div на motion.div:
Scroll Element
viewport={{ once: true }} гарантирует, что анимация выполнится только один раз при первом вхождении в экран.
В App.jsx можно рендерить несколько таких элементов динамически. Например:
let scrollElementCount=14;
{[...Array(scrollElementCount)].map((x, i) => )}
Теперь элементы будут анимироваться по мере прокрутки.
Альтернативы Framer Motion
Framer Motion не единственный инструмент. Вот основные альтернативы и сценарии, когда стоит рассмотреть их:
- React Spring — декларативная библиотека с физически корректными пружинными анимациями; хорошо подходит для сложных физических движений.
- CSS‑анимации / transition — нативная поддержка браузера, минимальная зависимость, хорошая производительность для простых эффектов.
- GreenSock (GSAP) — очень мощен для сложных и синхронизированных анимаций, но добавляет контейнер синтаксиса вне React‑конвенций.
Выбор зависит от целей: если вам нужна тесная интеграция с React‑стейтом и удобные lifecycle‑эффекты — Framer Motion часто удобнее. Если важна минимизация зависимостей — выбирайте CSS или Web Animations API.
Почему анимации улучшают UX
Анимации помогают пользователю понять, что произошло: появились новые элементы, данные обновились или произошло подтверждение действия. Они делают интерфейс «живым» и понятным. Главное — использовать умеренно и предсказуемо: слишком много эффектов отвлекает и замедляет взаимодействие.
Практические рекомендации и чек‑листы
Чек‑лист для разработчика:
- Используйте motion‑компоненты вместо прямых манипуляций с DOM.
- Выносите варианты состояний в const variants = {…} для читаемости.
- Применяйте AnimatePresence для компонентов, которым нужен exit.
- Проверяйте перформанс: избегайте анимации свойств, которые приводят к reflow (например, top/left); отдавайте предпочтение transform и opacity.
- Тестируйте на мобильных устройствах и низкой мощности.
Чек‑лист для дизайнера:
- Определите цель каждой анимации (подсказка, подтверждение, фокус).
- Ограничьте продолжительность анимаций для основных действий (обычно 100–400 мс для интерфейсных переходов).
- Укажите easing (spring/linear/cubic) и ожидаемое поведение.
Чек‑лист для QA:
- Проверить входные и выходные анимации на всех поддерживаемых браузерах.
- Убедиться, что exit анимации выполняются перед удалением DOM элементов.
- Проверить взаимодействие с клавиатурой и экранными читалками.
Accessibility и настройки для пользователей
Important: всегда уважайте системные настройки пользователя. Если у пользователя включён prefers‑reduced‑motion, отключайте ненужные анимации или упрощайте их.
Пример проверки prefers‑reduced‑motion в CSS/JS:
- CSS: @media (prefers-reduced-motion: reduce) { / упрощённые стили / }
- JS: const prefersReduce = window.matchMedia(‘(prefers-reduced-motion: reduce)’).matches;
Если prefersReduce === true, переключайте значения анимаций на статические состояния или устанавливайте transition.duration = 0.
Когда анимации вредны — контрпримеры
- Длительные декоративные анимации на главной странице замедляют восприятие и увеличивают TTI (Time To Interactive).
- Сложные последовательные анимации могут мешать пользователям с когнитивными ограничениями.
- Анимация layout‑свойств (width/height/top) на больших элементах приводит к сильному reflow и падению FPS.
Критерии приёмки
- Все ключевые сценарии имеют входную и выходную анимацию, если это необходимо.
- Exit‑анимации завершаются до удаления компонента из DOM (AnimatePresence обеспечивает это).
- Анимации не ухудшают перформанс (проверено на мобильных устройствах).
- Пользователь с prefers‑reduced‑motion вынужден не чувствовать лишних движений.
- Компоненты воспроизводимы и имеют понятные variants.
Быстрый чек‑шит (cheat sheet)
- Для hover: whileHover={{ scale: 1.05 }}
- Для tap: whileTap={{ scale: 0.95 }}
- Для входа/выхода: initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
- Для появлений поスクроллу: whileInView + viewport={{ once: true }}
- Для последовательных анимаций: useAnimation + controls.start(…)
Пример использования AnimatePresence с задержкой:
{isOpen && (
...
)}
Тестовые сценарии и критерии приёмки
- Нажать кнопку «Show Modal» — модалка появляется с ожидаемой анимацией.
- Нажать фон (backdrop) — модалка выходит с анимацией и затем удаляется из DOM.
- Навести курсор на кнопку — whileHover работает и возвращает элемент в исходное состояние при уходе.
- Прокрутить страницу — элементы с whileInView анимируются единожды.
- Включён prefers‑reduced‑motion — анимации не выполняются или используются минимальные переходы.
Краткая справка (глоссарий)
- motion — базовый конструкт для анимируемых компонентов.
- variants — набор именованных состояний анимации.
- AnimatePresence — компонент для корректных exit‑анимаций.
- whileInView — запускает анимацию при вхождении в viewport.
Заключение
Framer Motion даёт удобный декларативный API для анимаций в React, упрощая работу с интеракциями, модальными окнами и анимациями при прокрутке. Он помогает интегрировать анимации с React‑стейтом и уменьшает количество вспомогательного кода. В то же время важно соблюдать принципы производительности и доступности: отдавайте предпочтение transform/opacity, уважайте prefers‑reduced‑motion и тестируйте на целевых устройствах.
Короткие рекомендации:
- Начинайте с простых эффектов и выносите состояния в variants.
- Используйте AnimatePresence для выхода компонентов.
- Тестируйте перформанс и доступность.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone