Анимации при прокрутке бывают трёх видов: появление блоков при входе в экран, движение, привязанное к положению скролла, и видео, кадры которого следуют за прокруткой вперёд и назад. Хорошая scroll-анимация двигает только transform и opacity, проигрывает появление один раз и выключается у тех, кто включил в системе «уменьшить движение». Если без анимации текст нельзя прочитать или до кнопки заявки нужно «докрутить» эффект — это ошибка, а не приём.
После материала вы сможете
- различить три семейства scroll-анимаций: появление при входе, привязку к прокрутке и scrub-видео
- выбрать реализацию: IntersectionObserver, CSS scroll-driven animations с проверкой через @supports или одно обновление на кадр через requestAnimationFrame
- объяснить, почему для scrub-видео каждый кадр делают ключевым
- заложить запасной вариант для prefers-reduced-motion и определить экраны, где анимация при прокрутке не нужна
Что знать заранее
Содержание
Три семейства scroll-анимаций
В уроке о микроанимациях мы разобрали, зачем интерфейсу движение вообще. Здесь — только то, что связано с прокруткой. Под словами «анимация при скролле» прячутся три разных механизма: они по-разному устроены, по-разному нагружают телефон и требуют разных запасных вариантов.
- 01Появление при входеБлок входит в экран — один раз проигрывается короткая анимация. Прокрутка только запускает её.
- 02Привязка к прокруткеПоложение скролла становится шкалой времени: прокрутили наполовину — анимация на половине пути, в обе стороны.
- 03Scrub-видеоЧастный случай привязки: прокрутка двигает не CSS-свойства, а кадры видеоролика вперёд и назад.
Появление при входе
- Что управляет
- Сам факт: блок стал видимым
- Где уместно
- Почти любой сайт: карточки, кейсы, отзывы
- Главный риск
- Долгие задержки — текст «догоняет» читателя
Привязка к прокрутке
- Что управляет
- Положение скролла
- Где уместно
- Индикатор чтения, липкие сцены, рассказ о продукте
- Главный риск
- Нагрузка на телефон, укачивание, перехват прокрутки
Scrub-видео
- Что управляет
- Положение скролла превращается во время ролика
- Где уместно
- Один флагманский экран, где нужно показать устройство вещи
- Главный риск
- Тяжёлый файл и рывки при неправильной кодировке
Появление при входе в экран
Самый частый и самый безопасный вид. Блок изначально чуть прозрачен и немного сдвинут вниз, а когда входит в область видимости, плавно встаёт на место. Раньше для этого на каждое событие прокрутки пересчитывали положение элементов — это дорого. Сейчас стандартный инструмент — IntersectionObserver: браузер сам асинхронно сообщает, что элемент пересёк границу экрана, и не нужно слушать каждый пиксель прокрутки (подробно — в MDN).
Блоки появляются по мере прокрутки
- Проигрываем один раз: после появления перестаём следить за элементом, при прокрутке назад блок не прячется
- Сдвиг небольшой, движение короткое — появление не должно задерживать чтение
- Запускаем чуть раньше, чем блок полностью вошёл в экран, чтобы человек не видел пустое место
- Контент виден без JavaScript: если скрипт не загрузился, блоки не должны остаться прозрачными
Если в ряду несколько карточек, их удобно показывать каскадом (stagger): каждая следующая стартует с небольшой задержкой. Каскад подсказывает порядок чтения и делает сетку «живой». Но с длинной задержкой последняя карточка появляется так поздно, что её успевают пропустить.
Все сразу
Каскадом, шаг 60 мс
Анимация, привязанная к прокрутке
Во втором семействе прокрутка — не кнопка «старт», а ползунок времени. Прокрутили страницу на треть — анимация на трети пути, вернулись — она отматывается назад. Так работают индикатор прочитанного над статьёй, шапка, которая сжимается по мере прокрутки, и липкие сцены: секция закрепляется на экране через position: sticky, а пока человек крутит, внутри сменяются шаги рассказа.
Долгое время это делали только скриптами: на каждое событие прокрутки вычисляли прогресс и меняли стили. Теперь в CSS есть нативные scroll-driven animations: свойство animation-timeline связывает обычную CSS-анимацию со шкалой прокрутки контейнера (scroll()) или с тем, как конкретный элемент проходит через экран (view()). Chrome for Developers отмечает, что такие анимации могут выполняться вне основного потока — значит, не дёргаются, даже когда страница занята другой работой.
Индикатор чтения
Полоса над статьёй заполняется по мере прокрутки. Самый простой и полезный пример: человек видит, сколько осталось.
Липкая сцена
Секция закрепляется, а внутри по шагам меняются картинка и подпись. Хорошо для рассказа «как это работает» в несколько шагов.
Проявление по пути
Элемент проявляется, пока проходит через экран, и так же плавно уходит. В отличие от первого семейства, это обратимо.
Главное правило этого семейства — не перехватывать саму прокрутку. Страница должна ехать ровно так, как человек крутит колесо или ведёт пальцем: анимация следует за скроллом, а не подменяет его своим темпом.
Scrub-видео: кадры следуют за скроллом
Третье семейство — видео, которым управляет прокрутка. Ролик не играет сам: положение скролла переводится во время ролика (свойство currentTime видеоэлемента), и при прокрутке вниз кадры идут вперёд, а при прокрутке вверх — назад. Так можно разобрать предмет на части и собрать обратно, провести камеру по пространству, показать процесс, который трудно объяснить словами.
Главная техническая ловушка — кодировка. Обычный видеофайл хранит полный (ключевой) кадр лишь изредка, а между ключевыми — только изменения относительно соседних кадров. При прыжке в произвольную точку декодер вынужден вернуться к ближайшему ключевому кадру и пересчитать всё до нужного. При прокрутке назад это особенно заметно: видео дёргается или замирает. Поэтому для scrub-сцены ролик перекодируют так, чтобы ключевым был каждый кадр, — тогда любой кадр открывается сразу и в обе стороны.
Обычная кодировка
- Ключевые кадры редко, между ними — только разница
- Лёгкий файл, удобно для обычного просмотра
- Перемотка назад рывками: декодер возвращается к ключевому кадру
- Подходит для фонового видео, которое просто играет
Каждый кадр ключевой
- Любой кадр декодируется сам по себе
- Плавно вперёд и назад при любой скорости прокрутки
- Файл заметно тяжелее — вес нужно экономить другими способами
- Подходит для сцены, которой управляет скролл
- Короткий ролик: одна сцена, без лишних секунд в начале и в конце
- Умеренное разрешение и отдельная лёгкая версия для телефонов
- Без звуковой дорожки — в scrub-сцене она не нужна
- Постер-кадр показываем сразу, а ролик подгружаем, когда секция приближается к экрану
- Альтернатива — последовательность картинок на canvas: больше контроля, но и больше файлов
Как это устроено в демо-проектах InterLAB
В портфолио InterLAB есть демо-сайты, на которых мы отрабатываем эти механизмы. Это не заказы клиентов, а показательные проекты, но в каждом прокрутка отвечает на конкретный вопрос посетителя, а не просто украшает страницу.
- «Крем Coffee», демо кофейни: scrub-видео разбирает напиток на слои — видно, из чего он состоит, — а при прокрутке назад собирает его обратно.
- «Lumen Dental», демо стоматологии: прокрутка управляет 3D-срезом винира, и посетитель видит, как он устроен.
- «Нега SPA», демо спа-салона: прокрутка работает как камера, которая проходит по комнатам, — пространство можно «пройти» до визита.
Общее у всех трёх: один необычный механизм на страницу, а всё остальное — спокойное появление блоков. Цены, формы записи и контакты в этих демо не анимируются, а при включённом «уменьшить движение» главная сцена заменяется статичными кадрами.
Параллакс и производительность
Параллакс — слои страницы движутся с разной скоростью: фон медленнее, передний план быстрее, и появляется ощущение глубины. Nielsen Norman Group в тестах параллакса видела в основном равнодушных пользователей и набор проблем: страница грузится дольше, текст поверх движущегося фона читается хуже, а люди, которые быстро листают в поисках нужного, натыкаются на пустые экраны, потому что эффект не успел подгрузиться. Кроме того, крупное движение фона относительно переднего плана — один из типичных источников укачивания у людей с вестибулярными нарушениями.
Если параллакс всё же нужен, делайте его мягким: небольшая разница скоростей, только декоративные слои, текст — на неподвижной основе.
- Двигаем только transform и opacity — браузер обрабатывает их на этапе композиции, без пересчёта раскладки
- Не читаем размеры элементов в обработчике прокрутки вперемешку с записью стилей — это layout thrashing, принудительный пересчёт раскладки на каждом кадре
- Для появления — IntersectionObserver, для привязки — CSS scroll-driven animations или одно обновление на кадр через requestAnimationFrame
- Тяжёлые сцены запускаем, только когда секция рядом с экраном, и останавливаем, когда она ушла
- Проверяем на телефоне среднего класса, а не только на ноутбуке разработчика: там видно, где теряются кадры
Доступность и когда без анимации лучше
В операционных системах есть настройка «уменьшить движение», и сайт узнаёт о ней через медиазапрос prefers-reduced-motion (описан в MDN и web.dev). Критерий WCAG 2.3.3 «Анимация от взаимодействий» требует, чтобы движение, запущенное действием пользователя, — а прокрутка тоже действие — можно было отключить, если оно не является необходимым. Для scroll-анимаций это значит: при включённой настройке параллакс и привязанные сцены выключаются, блоки появляются простым проявлением или сразу, а вместо scrub-видео показывается статичный кадр или серия картинок с подписями.
Уместно
- Промо-страница продукта и рассказ «как это устроено»
- Флагманская секция кейса или главной страницы
- Индикатор чтения в длинной статье
- Мягкое появление карточек и отзывов
Не нужно
- Цены и тарифы — человек сравнивает, ему нужна неподвижная таблица
- Формы заявки и оплаты — движение рядом отвлекает и сбивает фокус
- Юридический текст: оферта, политика, условия
- Рабочие и справочные экраны, куда возвращаются часто
Прокрутка принадлежит посетителю. Анимация может идти за ней следом, но не должна её отбирать.
Источники
- MDN — Intersection Observer API
- MDN — CSS scroll-driven animations
- MDN — animation-timeline
- Chrome for Developers — Animate elements on scroll with scroll-driven animations
- MDN — HTMLMediaElement: currentTime
- Nielsen Norman Group — Parallax Scrolling: Attention-Getter or Headache?
- web.dev — How to create high-performance CSS animations
- web.dev — Avoid large, complex layouts and layout thrashing
- MDN — prefers-reduced-motion
- web.dev — prefers-reduced-motion: Sometimes less movement is more
- W3C WAI — Understanding SC 2.3.3: Animation from Interactions