Спроектируй систему easing и timing.
Основа: классические кривые
--linear: linear;
--ease: cubic-bezier(0.25, 0.1, 0.25, 1.0); /* CSS default */
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
Когда что
| Действие | Easing | Почему |
|---|---|---|
| Появление UI | ease-out | Старт быстро → замедление к финалу. Чувствуется как "прилёт" |
| Исчезновение UI | ease-in | Старт медленно → ускорение к финалу. Чувствуется как "ушло" |
| Continuous (drag, scroll) | linear | Естественное движение под палец |
| Циклическая (loading spinner) | linear | Постоянная скорость |
| Прыжок акцента | ease-in-out | Симметрично, для маятниковых движений |
Премиум-кривые (для специальных случаев)
/* Expo out — мощный appear effect */
--ease-expo-out: cubic-bezier(0.16, 1, 0.3, 1);
/* Back out — лёгкий "overshoot" перед остановкой */
--ease-back-out: cubic-bezier(0.34, 1.56, 0.64, 1);
/* Circ out — мягкое плавное замедление */
--ease-circ-out: cubic-bezier(0, 0.55, 0.45, 1);
Длительности (шкала)
--duration-instant: 100ms; /* Hover, focus */
--duration-fast: 150ms; /* Small UI changes */
--duration-normal: 200ms; /* Default */
--duration-medium: 300ms; /* Modal, dialog */
--duration-slow: 500ms; /* Page transitions */
--duration-spacious: 700ms; /* Hero animations */
Правила
- ≤ 100ms — мозг не воспринимает как "анимацию"
- 200ms — natural reaction time
-
500ms — пользователь начинает ждать
-
1000ms — почти всегда слишком долго
Размер влияет на длительность
Чем больше расстояние — тем дольше анимация (естественно):
Tooltip (10px движения): 150ms
Dropdown (50px): 200ms
Modal (центр экрана): 300ms
Page transition: 400-500ms
Stagger (каскад)
Когда анимируется группа — добавь задержку между элементами:
.item:nth-child(1) { animation-delay: 0ms; }
.item:nth-child(2) { animation-delay: 50ms; }
.item:nth-child(3) { animation-delay: 100ms; }
Stagger ≈ 50-80ms. Слишком мало — кажется одновременным. Слишком много — кажется ленивым.
Лимит: 6-8 элементов. Дальше — без stagger.
Spring physics
Альтернатива duration/easing. Параметры:
- stiffness — насколько "тугая" пружина
- damping — насколько "гасится" колебание
- mass — "вес" элемента
// Framer Motion
<motion.div animate={{x: 100}} transition={{type: "spring", stiffness: 300, damping: 30}} />
- Хорошо для интерактивных элементов (drag, swipe)
- Естественнее duration для физических метафор
- Сложнее предсказать (длительность зависит от расстояния)
Чёрный список
❌ ease-in-out для всего
❌ Bounce на UI кнопках
❌ Анимация width или height без will-change
❌ Анимация на transform: scale от 0 (создаётся нулевая bounding box)
❌ Длительности кратные 500ms — это "robotic"
Тест системы
Анимация хорошо настроена если:
- Пользователь не замечает её — только эффект
- Без неё интерфейс кажется "сломанным"
- Все анимации на сайте чувствуются одной семьёй
Принципы
- Меньше уникальных кривых — больше консистентность
- 3-4 easing'а + 4-5 длительностей — достаточно
- Симметричный enter/exit = ленивая работа. Asymmetric = professional
Микроанимации интерфейса
Hover, focus, активные состояния, toast'ы, переходы — мелкая моторика интерфейса.
Переходы между страницами
Когда нужны, какие выбрать, как не сделать сайт «торчащим».
Стратегия prefers-reduced-motion
Что отключать полностью, что заменять на fade, как тестировать и какие элементы НИКОГДА не должны быть motion-зависимыми.