CSS-эффекты и графика
Анимация по прокрутке
Анимация, привязанная к прокрутке, без единой строчки JS: animation-timeline с view() и scroll(). Появление блоков, наезд на картинку, параллакс, полоса прочитанного и сжатие шапки — с запасным путём для браузеров без поддержки.
Эффект
Класс, идентификатор или тег
Как это выглядит
Прокрутите внутри рамки.
Прокрутите вниз ↓
Конец
animation-timeline работает в Chrome и Edge с 115, в Safari с 26. В Firefox — за флагом, поэтому запасной путь не украшение, а условие: без него блок так и останется прозрачным.
CSS
@keyframes reveal {
from { opacity: 0; transform: translateY(32px); }
to { opacity: 1; transform: translateY(0); }
}
/* Блок всплывает и проявляется, пока въезжает снизу. */
.reveal {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% cover 60%;
}
/* Анимация по прокрутке — это движение по воле пользователя, но для тех,
кто просил меньше движения, оставляем конечное состояние без перехода. */
@media (prefers-reduced-motion: reduce) {
.reveal { animation: none; opacity: 1; transform: none; }
}
Запасной путь
Там, где таймлайна нет, блок с нулевой прозрачностью так и останется невидимым. Эти два куска включаются только при отсутствии поддержки и ничего не делают там, где она есть.
fallback.css
@supports not (animation-timeline: view()) {
.reveal {
opacity: 0;
transform: translateY(32px);
transition: opacity 0.5s ease, transform 0.5s ease;
}
.reveal.is-visible {
opacity: 1;
transform: none;
}
}
fallback.js
// Запасной путь для браузеров без animation-timeline: класс вешается,
// когда блок показался. Проверка идёт по поддержке, а не по названию браузера.
if (!CSS.supports('animation-timeline: view()')) {
const targets = document.querySelectorAll('.reveal');
const observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
entry.target.classList.add('is-visible');
observer.unobserve(entry.target);
}
}, { rootMargin: '0px 0px -10% 0px', threshold: 0.1 });
targets.forEach((el) => observer.observe(el));
}