Дорогое движение отличается от дешёвого не эффектом, а кривой ускорения и длительностью: элемент стартует быстро и мягко останавливается, а не едет с постоянной скоростью, как на конвейере. Для появления чаще всего подходит ease-out, для ухода с экрана — ease-in, для перемещения между двумя точками — ease-in-out, а большинство интерфейсных анимаций укладывается в 100–400 мс в зависимости от расстояния и размера элемента. Чтобы весь продукт двигался в одном ритме, команда фиксирует несколько именованных длительностей и кривых как токены дизайн-системы.
После материала вы сможете
- прочитать запись cubic-bezier и по форме кривой определить ease-in, ease-out или ease-in-out
- выбрать кривую под тип движения: появление, уход, перемещение, следование за жестом, бесконечное вращение
- подобрать длительность по расстоянию и размеру элемента и сделать исчезновение короче появления
- задать набор токенов движения — длительности и кривые — для дизайн-системы
Что знать заранее
Содержание
Почему линейное движение выглядит механическим
В уроке про микроанимации мы разобрали, зачем интерфейсу движение. Здесь — следующий шаг: как сделать так, чтобы оно ощущалось качественным. Главный инструмент — easing, или кривая ускорения: она описывает, как скорость элемента меняется от начала до конца анимации. Без неё (linear) элемент с первого кадра несётся на полной скорости и так же резко замирает.
web.dev формулирует это просто: в природе ничто не движется линейно из точки в точку, поэтому линейное движение кажется роботизированным. В докладе Apple о пружинных анимациях видно и почему: на графике скорости у линейной анимации резкий скачок в начале и в конце. Глаз считывает этот скачок как рывок — и интерфейс начинает выглядеть как дешёвая презентация.
linearease-inease-outease-in-outspringВход, выход и перемещение: какую кривую выбрать
Названия кривых сначала путают: ease-out используют, когда что-то появляется, а ease-in — когда уходит. Nielsen Norman Group объясняет логику так: ease-out на входе замедляет элемент перед остановкой, и глаз успевает предсказать, где он замрёт; ease-in на выходе разгоняет элемент, и кажется, что он уезжает за пределы экрана сам.
Элемент появляется: окно, подсказка, меню
- Кривая
- ease-out
- Как ощущается
- Быстрый отклик и мягкая остановка — интерфейс кажется отзывчивым
Элемент уходит с экрана насовсем
- Кривая
- ease-in
- Как ощущается
- Разгон и уход — не задерживает взгляд на том, что больше не нужно
Элемент перемещается внутри экрана
- Кривая
- ease-in-out
- Как ощущается
- Плавный старт и плавная остановка — как предмет, который передвинули рукой
Элемент следует за пальцем или курсором
- Кривая
- Без кривой, 1:1 с жестом
- Как ощущается
- Задержка или сглаживание здесь воспринимаются как «лаг»
Бесконечное вращение индикатора загрузки
- Кривая
- linear
- Как ощущается
- Равномерный поворот — единственный случай, где линейность уместна
cubic-bezier: что показывает кривая
Любую из стандартных кривых можно записать как cubic-bezier — функцию с четырьмя числами. По MDN это график, где по горизонтали — прошедшая доля времени, по вертикали — пройденная доля пути. Начало (0, 0) и конец (1, 1) зафиксированы, а четыре числа задают две промежуточные опорные точки, которые «тянут» кривую.
- Крутой подъём в начале — элемент стартует быстро; пологий участок в конце — мягкая остановка. Это и есть ease-out
- Пологое начало и крутой конец — медленный разгон и резкий финиш, то есть ease-in
- Кривая в форме буквы S — ease-in-out: разгон, быстрая середина, торможение
- Если вторая координата точки больше 1 или меньше 0, элемент перелетает цель и возвращается — так в CSS делают лёгкую «отдачу»
- Стандартные значения описаны в спецификации W3C: ease-in — cubic-bezier(0.42, 0, 1, 1), ease-out — cubic-bezier(0, 0, 0.58, 1), ease-in-out — cubic-bezier(0.42, 0, 0.58, 1)
Стандартные ключевые слова довольно мягкие. На проектах InterLAB мы обычно подбираем свою, более выраженную кривую появления — с резким стартом и длинным плавным хвостом: при той же длительности интерфейс ощущается быстрее. Подбирать удобно прямо в браузере: в Chrome DevTools у любого transition в панели Styles есть Easing Editor, где опорные точки можно тянуть мышью и сразу видеть результат.
Пружины: перелёт и затухание
Пружинная анимация (spring) описывается не кривой и временем, а физикой: насколько упругая пружина и насколько сильно её гасит трение. Apple в докладе WWDC 2023 упрощает настройку до двух параметров — длительности и «отскока» (bounce). При нулевом отскоке получается плавное движение без перелёта, при положительном элемент перелетает цель и несколько раз затухающе колеблется вокруг неё.
Главное преимущество пружины не в отскоке. Она умеет подхватить скорость, с которой человек отпустил элемент пальцем, и продолжить движение без рывка, а если цель поменялась на полпути — плавно развернуться. Поэтому в SwiftUI пружина стала анимацией по умолчанию. В вебе пружину можно приблизить CSS-функцией linear() по набору точек (её описывает MDN) — именно так сделана дорожка spring в демо выше.
Пружина уместна
- Перетаскивание карточек, свайпы, шторки — движение продолжает жест
- Анимация, которую могут прервать на середине
- Небольшой перелёт для приятного акцента: лайк, добавление в корзину
- Продукты с игровым, дружелюбным характером
Пружина лишняя
- Пружинит всё подряд: меню, кнопки, тексты, модальные окна
- Серьёзные интерфейсы: финансы, медицина, CRM, где важна точность
- Частые действия, которые человек повторяет десятки раз в день
- Заметный отскок на крупных блоках — экран начинает «трясти»
Длительность: расстояние, размер, вход и выход
Nielsen Norman Group называет диапазон 100–500 мс для большинства анимаций, а точное значение зависит от сложности изменения и от того, какое расстояние проходит элемент. Нажмите «Повторить» и посмотрите, где для вас проходит граница между «мгновенно», «плавно» и «приходится ждать».
100ms200ms300ms500ms1000ms~100 мс~100 мс
простая обратная связь: галочка, переключатель (NN/g)
200–300 мс200–300 мс
заметные изменения, например появление модального окна (NN/g)
до 400 мсдо 400 мс
крупные перемещения через большой экран (NN/g)
- Чем меньше элемент и короче путь, тем быстрее движение: подсказка у курсора и боковая панель на весь экран не могут жить с одной длительностью
- Исчезновение делайте короче появления: по NN/g окно может открываться за 300 мс и закрываться за 200–250 мс
- Чем чаще повторяется действие, тем короче или незаметнее анимация — Apple HIG советует не нагружать движением частые взаимодействия
- Анимация не должна блокировать: если человек уже кликает дальше, интерфейс не ждёт окончания эффекта
- Даже небольшая разница ощутима: NN/g отмечает, что переход с 250 на 300 мс может ощущаться совсем иначе
Токены движения: один ритм на весь продукт
Когда каждый разработчик подбирает длительность «на глаз», в продукте появляются 180, 250, 320 и 600 мс с пятью разными кривыми — и интерфейс начинает ощущаться неряшливым, хотя никто не может сказать почему. Решение — договориться о небольшом наборе именованных значений, токенов, и использовать только их, как цвета и отступы из палитры.
duration-fast
- Значение
- 100 мс
- Для чего
- Нажатия, переключатели, наведение
duration-base
- Значение
- 200 мс
- Для чего
- Подсказки, выпадающие меню, исчезновения
duration-moderate
- Значение
- 300 мс
- Для чего
- Модальные окна и панели на появление
duration-slow
- Значение
- 400 мс
- Для чего
- Крупные перемещения через весь экран
ease-enter
- Значение
- cubic-bezier(0, 0, 0.2, 1)
- Для чего
- Появление: быстрый старт, мягкая остановка
ease-exit
- Значение
- cubic-bezier(0.4, 0, 1, 1)
- Для чего
- Уход с экрана: разгон
ease-move
- Значение
- cubic-bezier(0.4, 0, 0.2, 1)
- Для чего
- Перемещение между точками на экране
spring-soft
- Значение
- пружина почти без отскока
- Для чего
- Жесты: свайпы, перетаскивание, шторки
В коде это одна строка: transition: transform var(--duration-moderate) var(--ease-enter). На проектах InterLAB токены движения живут рядом с цветами и шрифтами в дизайн-системе: дизайнер в макете пишет «появление — moderate, ease-enter», разработчик берёт ровно эти значения, и спорить о «чуть побыстрее» приходится один раз, а не на каждом экране.
Частые ошибки и как проверить движение
- Слишком медленно: анимации по 600–800 мс на кнопках и меню — сайт кажется тормозным
- Всё пружинит: отскок на каждом элементе перестаёт радовать уже на втором экране
- Разные кривые на одном экране: меню въезжает с ease-out, соседняя панель — линейно
- Одинаковая длительность для подсказки и для полноэкранной панели
- Выход такой же долгий, как вход, — закрытое окно всё ещё «висит» перед глазами
- Анимацию нельзя прервать: пока эффект не закончится, кнопки не реагируют
Замедлите
В Chrome DevTools откройте панель Animations: она записывает анимации страницы и позволяет проиграть их в несколько раз медленнее. На замедленном видно рывки, перелёты и разнобой кривых.
Поправьте кривую
В Easing Editor на панели Styles подтяните опорные точки и сразу посмотрите результат, а найденное значение оформите токеном.
Проверьте на телефоне
Пройдите ключевые сценарии на недорогом смартфоне: то, что плавно на ноутбуке, может дёргаться на слабом устройстве.
Посмотрите «свежим глазом»
Попросите коллегу, который не видел макет, пять раз открыть и закрыть меню. Если он начал ждать анимацию — она слишком длинная.
Хорошо настроенное движение замечают, только когда его убирают: интерфейс сразу становится резким или вязким.
Источники
- Nielsen Norman Group — Executing UX Animations: Duration and Motion Characteristics
- web.dev — The basics of easing
- web.dev — Choosing the right easing
- web.dev — Custom easing
- MDN — cubic-bezier()
- MDN — linear()
- W3C — CSS Easing Functions Level 2
- Apple — WWDC23: Animate with springs
- Apple Human Interface Guidelines — Motion
- Chrome DevTools — Inspect animations