Параллакс

Одно свойство на блок — --gr-progress от положения на экране. Эффект — открытый набор в CSS, а не перечень в скрипте: сдвиг, проявление, масштаб, поворот, полоса прогресса — что угодно из одного числа.

GriffinJS packages/ui/src/griffinjs/widgets/parallax.js

Как работает

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
--gr-progress:

Примеры использования

Каждый рецепт — один блок с 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%);
}
Сдвиг фона нужен griffinjs.js
Фон отстаёт

Проявление и подъём

Заголовок проявляется и подъезжает, пока блок входит в первую треть пути: 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);
}
Проявление нужен griffinjs.js
Появляюсь снизу

Масштаб через центр экрана

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));
}
Масштаб нужен griffinjs.js
Крупнее в центре

Поворот и цвет

Прогресс годится не только для движения: карточка слегка поворачивается и её подложка плотнеет — оба значения из той же переменной.

.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));
}
Поворот и плотность нужен griffinjs.js
Карточка

Полоса прочитанного

Длинная статья с индикатором: --gr-progress всей статьи превращается в ширину полосы. Та же полоса подходит слайдеру — там дорожка пишет ту же переменную на себя.

<article data-gr-parallax>
  <div class="px-bar"></div>
  …
</article>

.px-bar::before { inline-size: calc(var(--gr-progress, 0) * 100%); }