Анимация на сайте либо незаметно помогает человеку понять, что происходит, либо раздражает рывками и лишним движением. Разница почти никогда не в фантазии дизайнера: она в том, какие свойства анимируются, сколько работы при этом достаётся браузеру и уложился ли он в бюджет одного кадра. Ниже — то, что мы проверяем в первую очередь, когда на проекте жалуются на дёрганые переходы, и порядок действий, который приводит анимацию в порядок без переделки макета.
Откуда берутся рывки
Браузер старается обновлять картинку примерно 60 раз в секунду, то есть на один кадр у него около 16 миллисекунд. За это время он должен пересчитать стили, при необходимости заново разложить элементы на странице, перерисовать их и собрать слои в итоговое изображение. Чем больше этих шагов затрагивает анимация, тем выше шанс не уложиться в кадр. Пропущенный кадр глаз замечает как подёргивание, а несколько подряд — как залипание интерфейса.
Если элемент меняет ширину или отступ, браузер вынужден пересчитать положение соседей, а иногда и всей страницы. Если элемент меняет только смещение или прозрачность, работа сводится к сборке уже готовых слоёв — это самая дешёвая операция из всех. Отсюда простое правило, которым мы пользуемся в проектах: движение выражаем через transform и opacity, всё остальное применяем осознанно и на небольших элементах.
Что анимировать дёшево, а что дорого
| Свойство | Что пересчитывает браузер | Вывод для практики |
|---|---|---|
| transform: сдвиг, масштаб, поворот | Только сборку слоёв | Основной инструмент для любого движения |
| opacity | Только сборку слоёв | Появление, исчезание, смена состояний |
| color, background-color | Перерисовку самого элемента | Допустимо на кнопках, ссылках, иконках |
| box-shadow, filter | Перерисовку, часто большой области | Заменяем наложенным слоем с opacity |
| width, height, padding, margin | Раскладку целой ветки документа | Основная причина рывков, избегаем |
| top, left без transform | Раскладку и перерисовку | Заменяем на сдвиг через transform |
Сдвиг блока на двадцать пикселей и увеличение его отступа на те же двадцать пикселей выглядят для человека одинаково, а стоят для браузера принципиально по-разному. Если в проекте уже есть анимация из нижних строк таблицы и переписывать её некогда, первым делом мы сокращаем длительность: короткое подёргивание заметно меньше длинного.
Длительность, кривая и задержка
Три параметра решают, будет анимация выглядеть уместной или навязчивой. Ориентиры, от которых мы отталкиваемся:
- мелкие отклики — подсветка кнопки, смена иконки, изменение рамки поля — 100-150 миллисекунд;
- появление и скрытие блоков, выпадающие списки, всплывающие подсказки — 200-300 миллисекунд;
- крупные переходы, модальные окна, боковые панели — до 400 миллисекунд;
- всё, что дольше половины секунды, воспринимается как задержка интерфейса, а не как эффект.
Характер движения задаёт кривая. Замедление к концу подходит для появления: объект прилетает и мягко останавливается. Ускорение в начале — для исчезания: элемент уходит и не отвлекает. Симметричная кривая уместна для перемещения между двумя видимыми состояниями. Линейное движение выглядит механическим, его оставляют для бесконечных индикаторов загрузки и бегущих строк.
Задержки в интерфейсе почти всегда лишние. Единственный оправданный случай — последовательное появление элементов списка со сдвигом в 30-50 миллисекунд, и то при условии, что элементов не больше пяти-шести. Каскад из двадцати карточек превращается в ожидание.
Типовые задачи и рабочие решения
Раскрытие блока неизвестной высоты
Классическая задача: аккордеон, ответ в разделе вопросов, скрытые подробности карточки. Анимация высоты от нуля к автоматическому значению не работает, а фиксированная высота ломается на длинном тексте и при смене шрифта. Рабочее решение — анимировать сеточную строку от нулевой доли к единице, оставив внутреннему контейнеру скрытие переполнения. Высота остаётся автоматической, переход плавным, а вёрстка не зависит от объёма текста.
Появление блоков при прокрутке
Появление отслеживаем наблюдателем пересечений, а не обработчиком прокрутки: обработчик срабатывает десятки раз в секунду и сам становится источником подтормаживаний. Важная деталь: блок должен быть виден, даже если скрипт не выполнился. Мы задаём начальное скрытое состояние из скрипта, а не в стилях, иначе при ошибке в JavaScript половина страницы останется пустой.
Наведение и фокус
Всё, что реагирует на наведение, должно реагировать и на фокус с клавиатуры — иначе человек, который ходит по странице табуляцией, не понимает, где находится. На сенсорных экранах наведения нет вовсе: если карточка раскрывается только по наведению, на телефоне она либо не раскроется, либо застрянет в раскрытом состоянии после касания. Мы разделяем такие правила по условию поддержки наведения и добавляем обработку касания.
Индикаторы ожидания
Бесконечное вращение или пульсация — единственное место, где линейная кривая уместна. Но индикатор имеет смысл показывать не сразу: если ответ приходит быстро, мелькнувший на долю секунды спиннер выглядит как сбой. Разумно показывать его после небольшой паузы и держать минимальное время, чтобы он не исчезал в тот же момент, когда появился.
Слои композиции: когда помогает, когда мешает
Браузер может вынести элемент на отдельный слой и анимировать его почти без затрат. Подсказать это можно свойством will-change или преобразованием по третьей оси. Приём рабочий, но у него есть цена: каждый слой занимает видеопамять, а на мобильных устройствах её немного. Мы применяем его точечно, к нескольким ключевым элементам, и снимаем подсказку после завершения анимации. Список из полусотни карточек, у каждой из которых стоит will-change, обычно работает хуже того же списка без него.
Уменьшенное движение и доступность
В операционных системах есть настройка уменьшения движения, и включают её не из любви к минимализму: у части людей крупные перемещения и параллакс вызывают тошноту и головокружение. Если настройка включена, мы сводим анимации к простой смене прозрачности или отключаем их вовсе, а бесконечно движущиеся элементы останавливаем. Это несколько строк стилей, которые пишутся один раз на проект.
Отдельно проверяем, что отключение анимации не ломает логику. Если блок раскрывается только по событию завершения перехода, а перехода нет, обработчик может не сработать никогда — и раздел останется закрытым навсегда.
Как мы проверяем анимацию
- Смотрим не на рабочем ноутбуке, а на среднем телефоне и с искусственным замедлением процессора в инструментах разработчика.
- Записываем производительность во время анимации и смотрим, появляются ли в отчёте пересчёты раскладки — если да, анимируется что-то из нижней части таблицы.
- Проходим страницу с клавиатуры: реагирует ли то же самое на фокус, не уводит ли анимация фокус за пределы экрана.
- Включаем режим уменьшенного движения и убеждаемся, что интерфейс остаётся рабочим.
- Открываем страницу с отключённым JavaScript и проверяем, что содержимое видно.
- Проверяем поведение при быстром повторе: наведение и увод курсора десять раз подряд не должны копить незавершённые переходы.
Частые ошибки
- Анимируются размеры и отступы вместо преобразований — самая распространённая причина рывков.
- Одна длительность на все случаи: подсветка кнопки за 400 миллисекунд ощущается как задержка отклика.
- Анимация работает на элементах, которых не видно на экране, и тратит ресурсы впустую.
- Появление при прокрутке скрывает содержимое от поисковых роботов и от людей с отключёнными скриптами.
- Параллакс на первом экране: он почти всегда конфликтует с плавностью прокрутки на мобильных устройствах.
- Ради одного эффекта в проект приезжает чужая библиотека анимаций, а вместе с ней сотни килобайт стилей.
Анимация — не украшение, а способ объяснить человеку, что изменилось на экране и куда смотреть. Если она этого не делает, её проще убрать: спокойный и быстрый интерфейс воспринимается лучше нарядного и подтормаживающего.
Если нужна не разовая правка, а интерфейс, где анимация продумана вместе с вёрсткой и скоростью загрузки, мы занимаемся этим постоянно: разработка сайтов.











