webroad.online
  1. 1Веб
  2. 2HTML
  3. 3CSS
  4. 4JavaScript
  5. 5TypeScript
  6. 6Git
  7. 7Инструменты
  8. 8React
  9. 9Стейт-менеджмент
  10. 10Next.js
  11. 11Формы
  12. 12Данные и бэкенд
  13. 13SEO
  14. 14Tailwind CSS
  15. 15Анимации
  16. 16Тестирование
  17. 17Архитектура
Анимации · Урок 2 из 4

Motion для React

motion.div, initial/animate/exit, AnimatePresence, variants и stagger, layout, прокрутка, пружины.

Обновлено

Что такое Motion

Motion (бывший Framer Motion) — самая популярная библиотека анимаций в React. Идея: вы заменяете HTML-тег его версией motion. и описываете состояния, а не шаги — Motion сам вычисляет анимацию между ними.

npm i motion
'use client'
import { motion } from 'motion/react'

export function Card() {
  return <motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.3 }} />
}

Компоненты motion.* — Client Components, в файле есть 'use client'. Страницу можно оставить Server Component и анимировать только маленькие части.

Базовые props

Prop Что делает
initial начальное состояние (при монтировании)
animate целевое состояние — когда оно меняется, Motion анимирует к нему
exit состояние при размонтировании (нужен AnimatePresence)
transition duration, ease, delay или type: 'spring' со stiffness / damping / bounce
whileHover, whileTap, whileFocus временные состояния при взаимодействии
whileInView состояние, когда элемент появляется на экране (+ viewport={{ once: true }})
layout автоматически анимирует изменения размера / позиции в раскладке
layoutId два разных элемента с одним id «превращаются» друг в друга

Уход: AnimatePresence

React сразу удаляет элемент из DOM, когда он больше не рендерится, — на анимацию нет времени. AnimatePresence держит его, пока не закончится exit:

<AnimatePresence>
  {open && (
    <motion.div key="panel" initial={{ opacity: 0, y: -8 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -8 }} />
  )}
</AnimatePresence>

Variants и stagger

Variants = именованные состояния, описанные один раз и передающиеся детям. Идеально для списков:

import { motion, stagger } from 'motion/react'

const list = { hidden: {}, show: { transition: { delayChildren: stagger(0.06) } } }
const item = { hidden: { opacity: 0, y: 16 }, show: { opacity: 1, y: 0 } }

<motion.ul variants={list} initial="hidden" whileInView="show" viewport={{ once: true }}>
  {products.map(p => <motion.li key={p.id} variants={item}>{p.name}</motion.li>)}
</motion.ul>

Детям не нужны initial / animate — они наследуют имена состояний от родителя.

Layout-анимации — эффект «вау» одной строкой

<motion.div layout className={expanded ? 'w-full' : 'w-40'} />     // размер анимируется автоматически
{tabs.map(tab => (
  <button key={tab} onClick={() => setActive(tab)} className="relative px-3 py-1">
    {active === tab && <motion.span layoutId="tab-indicator" className="absolute inset-0 rounded-md bg-hover" />}
    <span className="relative">{tab}</span>
  </button>
))}

Индикатор «скользит» от одной вкладки к другой — без расчёта позиций.

Прокрутка

const { scrollYProgress } = useScroll()                      // 0 → 1 по всей странице
const scaleX = useSpring(scrollYProgress)                     // сглажено пружиной
<motion.div style={{ scaleX }} className="fixed inset-x-0 top-0 h-1 origin-left bg-accent" />   // полоса прогресса

const y = useTransform(scrollYProgress, [0, 1], [0, -200])   // параллакс

useTransform = интерполяция: отображает входной диапазон на выходной.

Пружина или tween

Tween (duration + ease) Пружина
Длительность фиксированная следует из физики
Ощущение контролируемое, точное естественное, «живое»
Прерывание на середине может дёрнуться продолжает плавно, сохраняет скорость
Когда прозрачность, цвет, затухания движение, размер, перетаскивание, жесты

Коротко

  • motion.div + initial / animate / exit; AnimatePresence для ухода.
  • Variants + stagger() для списков; whileInView для появления при прокрутке.
  • layout / layoutId для переходов раскладки; useScroll + useTransform для прокрутки; пружины для движения.

Официальные источники

Упражнения

Была ли страница полезной?

Один клик — без регистрации.