~/wiki / motion-i-animatsiya / printsipy-animatsii-easing-duration-delay

Easing, duration, delay: три параметра которые делают анимацию живой, а не мультяшной

Основной чат

Чат для вайбкодеров: новости, гайды, поиск исполнителей, маркетплейс и разбор реальных кейсов.

$ cd раздел/ $ join vibe dev
Easing, duration, delay: три параметра которые делают анимацию живой, а не мультяшной - обложка

Можно взять правильный тип анимации, правильный компонент — и всё равно получить что-то механическое или мультяшное. Три параметра определяют насколько анимация ощущается как настоящая: easing, duration, delay.

Изменить любой из трёх — и та же анимация будет выглядеть совсем иначе. Разница между «профессиональный продукт» и «сделано за выходные» часто только в этих числах.


Easing: как движутся реальные объекты

Линейное движение не существует в природе. Брошенный мяч ускоряется. Катящийся шар замедляется. Дверь открывается быстро и плавно притормаживает. В UI переходы с кривыми easing всегда ощущаются более естественными — потому что они ведут себя как физические объекты.

Шесть кривых которые нужно знать

ease-out — быстрый старт, плавное торможение. Самый естественный для большинства UI. Имитирует объект который тормозит при остановке. Использовать для входящих элементов, появляющихся карточек, тостов.

ease-in — медленный старт, ускорение к концу. Объект набирает скорость и исчезает. Использовать для уходящих элементов, закрывающихся модалок, удаляемых элементов списка.

ease-in-out — замедление с обеих сторон. Для перемещений между состояниями одного элемента. Ощущается как объект который набирает и теряет скорость.

linear — постоянная скорость. Только для прогресс-баров и ротационных спиннеров. Везде остальное выглядит механически.

cubic-bezier(0.34, 1.56, 0.64, 1) — spring easing. Лёгкий перелёт за точку назначения и возврат. Создаёт ощущение упругости и жизни. Для появления иконок, success-анимаций, элементов которым нужна «живость». Не злоупотреблять — один раз на экран.

cubic-bezier(0.16, 1, 0.3, 1) — ease-out expo. Очень быстрый старт, долгое плавное завершение. Современный premium-feel. Используют Linear, Vercel, Notion.

css
/* Входящие элементы — ease-out expo */
.card-enter {
  animation: slideUp 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

/* Уходящие элементы — ease-in */
.card-exit {
  animation: slideDown 0.25s cubic-bezier(0.4, 0, 1, 1) forwards;
}

/* Физика — spring с перелётом */
.button:active {
  transform: scale(0.97);
  transition: transform 0.1s cubic-bezier(0.34, 1.56, 0.64, 1);
}

Почему easing важнее duration

Интересный эффект: ease-out с duration 350мс ощущается быстрее чем linear с duration 250мс. Потому что большая часть движения ease-out происходит в начале — пользователь видит результат быстро. Правильный easing важнее короткой duration.

Практическое следствие: если анимация кажется медленной — сначала проверь easing, потом уменьшай duration.


Duration: сколько длится

Правило: чем меньше элемент — тем быстрее анимация. Чем важнее действие — тем заметнее переход.

Тип анимации Duration
Tap/hover feedback 80–120мс
Микровзаимодействие 150–200мс
Показать/скрыть 200–300мс
Переход между экранами 300–400мс
Onboarding/storytelling 400–700мс

Самая частая ошибка: одна duration на всё. Кнопка которая реагирует на нажатие за те же 300мс что и переход на новую страницу — ощущается заторможенной.

Кнопка — маленький элемент, важный но не страница. 100–120мс. Модальное окно — большой элемент, структурное изменение. 250–300мс. Страница целиком — 350–400мс.

Проверить что duration правильный: воспроизведи анимацию и засеки время до момента когда хочется кликнуть дальше. Если хочется кликнуть раньше чем анимация закончится — она слишком долгая.


Delay и stagger: последовательность вместо хаоса

Delay — задержка перед стартом. Используется для последовательности: один элемент должен появиться раньше другого.

Stagger — нарастающий delay для группы. Формула: delay = index × interval.

css
.list-item:nth-child(1) { animation-delay: 0ms; }
.list-item:nth-child(2) { animation-delay: 60ms; }
.list-item:nth-child(3) { animation-delay: 120ms; }
.list-item:nth-child(4) { animation-delay: 180ms; }

/* Или через CSS custom property: */
.list-item {
  animation-delay: calc(var(--index) * 60ms);
}

Правила stagger:

  • 40–80мс между элементами — стандартный диапазон
  • Больше 100мс — уже заметно медленно для списков
  • Меньше 30мс — почти не считывается как stagger

Для больших списков (20+ элементов): diminishing stagger. Интервал уменьшается для поздних элементов чтобы последний не появлялся через секунды после первого:

javascript
// Stagger уменьшается с индексом
const delay = Math.min(index * 60, 400); // максимум 400мс

Как правильно передать параметры анимации разработчику

Самая частая причина потери анимации при хендоффе: дизайнер передаёт «примерно вот так» через прототип Figma, разработчик воспроизводит на глаз.

Что нужно передать явно:

plaintext
Анимация: появление карточки
- Duration: 350ms
- Easing: cubic-bezier(0.16, 1, 0.3, 1)
- Transform: translateY(16px) → translateY(0)
- Opacity: 0 → 1
- Trigger: при монтировании компонента
- Stagger (если список): 70ms между карточками

Числа нельзя «угадать по ощущению» — нужно передавать точно. Пять секунд на запись спасут час правок.


Промпт для Codex / Claude Code

markdown
Добавь анимации появления для компонентов на странице [путь].

Параметры:
- Карточки: opacity 0→1, translateY 20px→0
- Duration: 400ms
- Easing: cubic-bezier(0.16, 1, 0.3, 1)
- Stagger: 70ms между карточками
- Trigger: IntersectionObserver когда элемент входит во вьюпорт
- prefers-reduced-motion: отключить анимацию, показывать сразу

Не использовать setTimeout для stagger — только CSS animation-delay
или IntersectionObserver с разными порогами.
plaintext
Easing
☐ Входящие — ease-out или cubic-bezier(0.16, 1, 0.3, 1)
☐ Уходящие — ease-in
☐ Нет linear на UI-элементах (только прогресс/спиннер)

Duration
☐ Tap/hover: 80–150мс
☐ Показать/скрыть: 200–300мс
☐ Экранные переходы: 300–400мс

Stagger
☐ 40–80мс между элементами
☐ Для длинных списков: diminishing stagger (не больше 400мс суммарно)
☐ prefers-reduced-motion: показывать сразу без задержки

Как easing влияет на восприятие продукта

Один и тот же продукт с разными easing-кривыми воспринимается по-разному.

Linear easing во всём — ощущение дешёвого приложения. Движение механическое, как у компьютерной графики 90-х. Причина: мозг распознаёт линейное движение как неестественное.

ease-in-out везде — приятно но медленно. Каждая анимация «плывёт». Это часто встречается в продуктах которые «переусердствовали с плавностью».

ease-out для входящих, ease-in для уходящих — профессиональный стандарт. Движение ощущается физическим. Элементы «прилетают» и «улетают», а не просто появляются и исчезают.

Именно поэтому один из первых вопросов при оценке motion-качества продукта — «какой easing используется?». Это быстрый индикатор уровня внимания к деталям.


Stagger: когда добавлять, когда не добавлять

Stagger делает появление группы элементов читаемым — но только если элементов несколько и они однотипные. Три кейса где stagger работает хорошо: список карточек (3–12 штук), навигационные пункты при открытии меню, иконки в тулбаре.

Три кейса где stagger только мешает: два элемента (задержка слишком заметна), разнородные элементы (dagger создаёт иллюзию несвязанности), мобайл с медленным соединением (пользователь уже читает первую карточку, остальные ещё не появились).

Для мобайла: stagger 40–50мс (не 70–80мс как для десктопа). Меньший интервал — быстрее вся последовательность — меньше ощущение «ждать пока загрузится».


Практика: как найти правильные числа быстро

Нет универсального ответа на «какой duration?». Есть метод:

Начни с 300мс для любой анимации. Воспроизведи. Кажется медленной — уменьши до 200мс. Всё ещё медленно — 150мс. Кажется слишком быстрой — 350мс.

Для easing: начни с ease-out для всего. Если анимация кажется резкой при завершении — ease-in-out. Если нужна «живость» — cubic-bezier(0.34, 1.56, 0.64, 1).

Это не строгая наука — это итерация. Три-четыре варианта чтобы найти «правильное» ощущение. С опытом итераций становится меньше.


Частые ошибки при работе с duration

Одна duration на весь продукт. «У нас везде 300мс» — кнопка hover ощущается медленной, экранный переход ощущается быстрым. Нужны разные значения для разных контекстов.

Duration без easing. 200мс с linear выглядит хуже чем 300мс с ease-out. Если анимация кажется механической — сначала меняй easing, потом duration.

Stagger на двух элементах. Если элементов два — stagger только мешает. Задержка 70мс между двумя карточками заметна и воспринимается как баг, а не дизайн-решение.

Слишком большой stagger на длинных списках. 20 элементов × 70мс = 1.4 секунды до последнего. Для длинных списков используй diminishing stagger или ограничивай общее время до 400–500мс.

$ cd ../ ← назад к Motion и анимация