Параллакс
Одно свойство на блок — --gr-progress от положения на экране. Эффект — открытый набор в CSS, а не перечень в скрипте: сдвиг, проявление, масштаб, поворот, полоса прогресса — что угодно из одного числа.
Как работает
data-gr-parallax на любом блоке. Скрипт пишет на него
--gr-progress: 0 — верхний край блока только вошёл
снизу экрана, 1 — нижний край ушёл вверх, 0.5 — блок
в середине пути. Источник — IntersectionObserver, который включает
и выключает подписку на прокрутку, и один пересчёт на кадр. Ни
transform, ни opacity скрипт не пишет — только число.
<section class="hero-block" data-gr-parallax>…</section>
.hero-block .bg {
translate: 0 calc((var(--gr-progress, 0.5) - 0.5) * -40%);
}
Запасное значение в var(--gr-progress, 0.5) — «база без JS»:
без скрипта блок стоит в нейтральном положении. Там, где есть
scroll-driven animations, тот же CSS отдаётся платформе без смены
разметки — animation-timeline: view() вместо переменной.
Живое значение
--gr-progress нужен griffinjs.jsПримеры использования
Каждый рецепт — один блок с data-gr-parallax и несколько строк
CSS. Все они уважают prefers-reduced-motion: в этом режиме
свойства движения сброшены, а блок остаётся видимым.
Фон медленнее страницы
Классический параллакс: узор фона сдвигается на 40 % высоты в противоход
прокрутке. inset: -20% 0 у псевдоэлемента даёт запас, чтобы
края не оголялись.
.px-shift::before {
inset: -20% 0;
translate: 0 calc((var(--gr-progress, 0.5) - 0.5) * -40%);
}
Проявление и подъём
Заголовок проявляется и подъезжает, пока блок входит в первую треть пути:
clamp(0, progress × 3, 1) растягивает начало шкалы, чтобы
эффект закончился, когда блок ещё внизу экрана.
.px-reveal .px-title {
opacity: clamp(0, var(--gr-progress, 0.5) * 3, 1);
translate: 0 calc((1 - clamp(0, var(--gr-progress, 0.5) * 3, 1)) * 2rem);
}
Масштаб через центр экрана
1 − |progress − 0.5| × 2 — «колокол»: ноль по краям экрана,
единица в центре. Элемент растёт к середине пути и убывает к концу.
.px-zoom .px-title {
scale: calc(0.8 + 0.4 * (1 - abs(var(--gr-progress, 0.5) - 0.5) * 2));
}
Поворот и цвет
Прогресс годится не только для движения: карточка слегка поворачивается и её подложка плотнеет — оба значения из той же переменной.
.px-rotate .px-card {
rotate: calc((var(--gr-progress, 0.5) - 0.5) * 12deg);
background: hsl(0 0% 100% / calc(0.4 + var(--gr-progress, 0.5) * 0.5));
}
Полоса прочитанного
Длинная статья с индикатором: --gr-progress всей статьи
превращается в ширину полосы. Та же полоса подходит слайдеру — там
дорожка пишет ту же переменную на себя.
<article data-gr-parallax>
<div class="px-bar"></div>
…
</article>
.px-bar::before { inline-size: calc(var(--gr-progress, 0) * 100%); }