Анимации на CSS scroll-driven — без библиотек
Раньше для анимации по скроллу тянули GSAP, Lenis, ScrollTrigger — 50–120 КБ JS. В 2026 нативный CSS `animation-timeline: scroll()` и `view()` закрывают 80% задач без единой строки JS. Разбираем синтаксис, поддержку и реальные кейсы.
Скролл-анимации — отдельный мир: progress bar сверху, параллакс героев, ленивое появление карточек, sticky-секции с метаморфозами. До 2024 это значило тянуть GSAP + ScrollTrigger, Locomotive, Lenis, Framer Motion — от 40 до 150 КБ минифицированного JS, плюс прогрев main thread на каждый кадр.
В Chrome 115+, Edge 115+ и за Safari TP/Firefox 129+ работает CSS scroll-driven animations — нативная связка анимации с прогрессом скролла или видимостью элемента. На лендингах студии в 2026 это покрывает 80% сценариев без библиотек.
Что появилось
Два новых типа таймлайна для CSS-анимаций:
animation-timeline: scroll()— прогресс целого скролл-контейнера (страница или div с overflow).animation-timeline: view()— прогресс пересечения элемента с viewport, как IntersectionObserver, только в CSS.
Оба работают с обычным @keyframes. Никакого JS, никаких rAF-циклов, всё на композиторе.
Кейс 1: progress bar сверху
Классика — полоса прогресса чтения статьи.
.progress {
position: fixed;
top: 0; left: 0;
height: 3px;
background: #e63946;
transform-origin: 0 0;
animation: progress linear;
animation-timeline: scroll(root);
}
@keyframes progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
Семь строк. До этого — подписка на scroll, debounce, расчёт scrollHeight - clientHeight, requestAnimationFrame. Здесь — браузер сам.
Кейс 2: появление карточек при попадании в экран
view() привязывает прогресс анимации к пересечению элемента с viewport. Можно анимировать opacity и transform без IntersectionObserver.
.card {
opacity: 0;
transform: translateY(40px);
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% entry 50%;
}
@keyframes reveal {
to { opacity: 1; transform: none; }
}
animation-range: entry 0% entry 50% читается так: анимация идёт от момента, когда элемент только начал входить в viewport (0%), до момента, когда он зашёл на 50% высоты viewport.
Ключевые значения animation-range: cover (вся видимая фаза), contain (фаза, когда элемент полностью внутри), entry (вход), exit (выход).
Кейс 3: параллакс героя
.hero-bg {
animation: parallax linear;
animation-timeline: scroll(root);
animation-range: 0 100vh;
}
@keyframes parallax {
to { transform: translateY(-20%); }
}
Фон героя поднимается на 20% при первом экране скролла. Никаких rAF, никаких will-change подкручиваний.
Кейс 4: sticky-секция с морфингом
Самое сильное — пин-секции, где при скролле меняется состояние элемента: иконка превращается в логотип, цифра растёт, текст переезжает.
.stage {
position: sticky;
top: 0;
height: 100vh;
}
.stage h2 {
animation: stage-title linear;
animation-timeline: view();
animation-range: cover;
}
@keyframes stage-title {
0% { font-size: 1rem; opacity: 0.4; }
50% { font-size: 5rem; opacity: 1; }
100% { font-size: 1.5rem; opacity: 0.8; }
}
Поддержка в 2026
На июнь 2026:
- Chrome, Edge, Opera — стабильно с 115 (август 2023).
- Safari — 17.4+ (март 2024) частично, 18+ полноценно.
- Firefox — 129+ (август 2024) с флагом, 130+ по умолчанию.
Глобальный охват по caniuse — ~92%. Для landing студии этого достаточно. Для остального — graceful degradation: пользователь без поддержки видит финальное состояние без анимации.
Прогрессивное улучшение
Простой паттерн — feature query:
.card { opacity: 1; transform: none; }
@supports (animation-timeline: view()) {
.card {
opacity: 0;
transform: translateY(40px);
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% entry 50%;
}
}
Старый Safari — карточки сразу видны. Современный — плавное появление.
Когда брать JS-библиотеку
CSS scroll-driven не закрывают всё. JS остаётся нужен, когда:
- Нужно smooth scrolling (Lenis) — CSS не сглаживает сам трэкпад/колесо.
- Анимация по нескольким триггерам сразу (скролл + курсор + время).
- Sequence с зависимостями между несколькими элементами по timeline.
- Pin-секции с complex ease (overshoot, bounce между точками).
- Нужна поддержка Safari 16 и старше — там полифилл огромный.
Что прячут библиотеки
GSAP ScrollTrigger делает три вещи, которые CSS пока не умеет:
- Координация нескольких анимаций по одному progress.
- Колбэки на достижение точек (для аналитики, ленивой загрузки).
- Reverse-on-leave с инерцией.
Если задача — «логотип сделай помельче когда скроллит и плавно увеличь когда вернулся вверх», CSS справится через два animation-range. Если задача — «при достижении 30% запусти видео и подсветь третью карточку», нужен JS.
Перформанс
CSS scroll-driven работают на композиторе. Это значит: 60 FPS даже на бюджетных Android 2020 года, нулевая нагрузка на main thread, нет jank от React-перерисовок. Lighthouse не штрафует.
Сравните с типичным GSAP-параллаксом: каждый кадр JS пересчитывает offset, ставит inline-стиль, браузер перерисовывает. На странице из 20 карточек с reveal — гарантированный лаг на старых устройствах.
Грабли, на которые наступают
- Забыли
both. Безanimation-fill-mode: bothэлемент дёргается в начальное состояние при выходе из range. - Перепутали
scroll()иview().scroll()— прогресс контейнера,view()— прогресс конкретного элемента. Параллакс героя —scroll(root), появление карточки —view(). - Range в неправильных единицах.
entry 0% entry 50%— проценты от высоты viewport, не от размера элемента. - Анимация transform + opacity на <img>. Браузер не всегда композитит — иногда падает на main thread. Делайте transform на родителе.
Вывод
В 2026 нативные CSS scroll-driven закрывают reveal, progress bar, параллакс и sticky-морфинг без единой строки JS. GSAP остаётся только под сложные сценарии с координацией и колбэками. Для лендинга студии это означает минус 80–120 КБ JS, 60 FPS на любых устройствах и нулевую боль с lifecycle компонентов.