Применение для скроллинга

Скроллинг представляет собой непрерывное изменение числового значения, которое естественным образом подходит для управления анимацией. В основе подхода лежит преобразование текущей позиции прокрутки в параметры движения интерфейса: смещение, прозрачность, масштаб, скорость, фазу интерполяции.

Библиотека Popmotion предоставляет низкоуровневые примитивы для работы с потоками значений, физическими моделями и интерполяцией, что позволяет строить сложные scroll-driven интерфейсы без привязки к фреймворку.


Базовый поток scroll-события

Любая логика начинается с получения значения прокрутки. В веб-контексте это чаще всего window.scrollY, однако прямое использование значения приводит к избыточным перерасчётам и усложнению синхронизации анимаций.

Popmotion позволяет преобразовать поток событий в управляемый stream:

import { fromEvent, map } from 'popmotion';

const scrollY$ = fromEvent(window, 'scroll').pipe(
  map(() => window.scrollY)
);

Здесь формируется реактивный поток, который можно связывать с анимационными свойствами.


Интерполяция значений скролла

Скролл почти никогда не используется напрямую. Требуется преобразование диапазонов: от пикселей страницы к нормализованным значениям.

import { transform } from 'popmotion';

const mapScroll = transform([
  [0, 500],
  [0, 1]
]);

Теперь любое значение прокрутки можно привести к диапазону [0, 1], который удобно использовать для анимации opacity, scale или transform.


Привязка скролла к анимационным свойствам

Связывание scroll-значения с визуальным состоянием чаще всего строится через onUpdate и ручное применение стилей.

scrollY$.subscribe((y) => {
  const progress = mapScroll(y);

  element.style.opacity = 1 - progress;
  element.style.transform = `translateY(${progress * 100}px)`;
});

Такой подход формирует прямую зависимость между прокруткой и состоянием DOM-элемента.


Сглаживание и инерция

Резкие изменения scroll создают визуальный шум. Для его устранения применяется сглаживание через spring-модель или инерционный фильтр.

Popmotion содержит физические модели, позволяющие имитировать естественное движение.

import { spring } from 'popmotion';

spring({
  from: 0,
  to: 1,
  stiffness: 80,
  damping: 20
}).start((v) => {
  element.style.transform = `scale(${v})`;
});

При использовании со scroll-значениями spring часто применяется как промежуточный слой между текущим значением и визуальным состоянием.


Scroll-driven параллакс

Параллакс основан на различной скорости движения слоёв относительно прокрутки. Это достигается масштабированием scroll-значения с разными коэффициентами.

scrollY$.subscribe((y) => {
  const backgroundOffset = y * 0.3;
  const foregroundOffset = y * 0.8;

  bg.style.transform = `translateY(${backgroundOffset}px)`;
  fg.style.transform = `translateY(${foregroundOffset}px)`;
});

Popmotion позволяет структурировать такие зависимости через transform pipelines, избегая дублирования вычислений.


Ключевые диапазоны и breakpoints

Скролл часто разбивается на логические сегменты, внутри которых поведение анимации отличается. Это реализуется через условные трансформации значений.

const sectionProgress = transform([
  [0, 300, 0],
  [300, 800, 1],
  [800, 1200, 0.5]
]);

Каждый сегмент задаёт собственную интерполяцию, создавая сложные сценарии поведения интерфейса.


Scroll как таймлайн анимации

Скролл может заменять время как основную ось анимации. В этом случае прогресс определяется позицией страницы, а не clock-based интервалом.

scrollY$.subscribe((y) => {
  const t = Math.min(y / 1000, 1);

  element.style.transform = `
    translateX(${t * 300}px)
    rotate(${t * 360}deg)
  `;
});

Такая модель позволяет синхронизировать несколько элементов с одной временной осью, управляемой прокруткой.


Scroll-linked physics animation

Popmotion позволяет объединять физические модели с scroll input. Это создаёт эффект инерционного следования за прокруткой.

import { tween } from 'popmotion';

let current = 0;

scrollY$.subscribe((y) => {
  tween({
    from: current,
    to: y,
    duration: 300
  }).start((v) => {
    current = v;
    element.style.transform = `translateY(${v}px)`;
  });
});

Такой подход создаёт задержку реакции, визуально отделяя пользовательское действие от результата.


Scroll-based reveal анимации

Появление элементов при прокрутке строится на пороговых значениях viewport.

scrollY$.subscribe((y) => {
  const trigger = 600;

  const opacity = y > trigger ? 1 : 0;
  const offset = y > trigger ? 0 : 40;

  element.style.opacity = opacity;
  element.style.transform = `translateY(${offset}px)`;
});

Popmotion позволяет заменить условную логику на непрерывные функции интерполяции, уменьшая дискретность переходов.


Преобразование viewport-relative значений

Scroll сам по себе не учитывает размеры viewport. Для адаптивного поведения вводятся относительные коэффициенты:

const vh = window.innerHeight;

const progress = transform([
  [0, vh, 0],
  [vh, vh * 2, 1]
]);

Это создаёт поведение, зависящее от экрана устройства, а не от абсолютных пикселей.


Комбинация нескольких источников движения

Scroll часто комбинируется с pointer, hover или drag input. Popmotion позволяет объединять потоки в единый контроллер состояния.

import { merge } from 'popmotion';

const combined$ = merge(scrollY$, pointerX$);

combined$.subscribe(([scroll, pointer]) => {
  element.style.transform = `
    translate(${pointer}px, ${scroll}px)
  `;
});

Такая композиция создаёт многомерное управление интерфейсом.


Производительность и оптимизация вычислений

Scroll-события могут генерироваться с высокой частотой. Для снижения нагрузки применяется throttling и requestAnimationFrame синхронизация.

let ticking = false;

window.addEventListener('scroll', () => {
  if (!ticking) {
    requestAnimationFrame(() => {
      const y = window.scrollY;
      element.style.transform = `translateY(${y}px)`;
      ticking = false;
    });

    ticking = true;
  }
});

Popmotion абстрагирует подобные механизмы через внутренние scheduling стратегии, снижая необходимость ручного контроля.


Scroll как система координат интерфейса

При расширенном применении скролл перестаёт быть событием и становится координатной системой. Элементы интерфейса размещаются не в фиксированном DOM-положении, а в зависимости от scroll-space.

Каждый компонент получает функцию:

f(scrollY) → visual state

и обновляется непрерывно в зависимости от глобального положения страницы.


Композиция scroll-анимаций через трансформации

Popmotion позволяет строить цепочки преобразований, где scroll проходит через несколько стадий:

scrollY → normalize → clamp → spring → render

Каждый этап отвечает за отдельную часть поведения: нормализацию диапазона, ограничение значений, физическое сглаживание и отрисовку.

const pipeline = transform([
  [0, 2000, 0, 1]
]);

Такая композиция упрощает масштабирование сложных интерфейсов.


Декоративные эффекты на основе скорости скролла

Помимо позиции, используется производная величина — скорость изменения scroll.

let lastY = 0;
let velocity = 0;

scrollY$.subscribe((y) => {
  velocity = y - lastY;
  lastY = y;

  element.style.filter = `blur(${Math.abs(velocity) * 0.02}px)`;
});

Это создаёт эффекты размытия, смещения или динамического искажения, зависящие от темпа прокрутки.


Scroll-synced timeline для сложных сцен

При построении сложных интерфейсов scroll используется как глобальный таймлайн, к которому привязываются сцены.

Каждая сцена определяет:

  • диапазон активации
  • входные трансформации
  • выходные эффекты
const scene1 = transform([[0, 500, 0, 1]]);
const scene2 = transform([[500, 1000, 0, 1]]);

Это позволяет разбивать длинные страницы на независимые анимационные блоки.


Многослойные сцены и глубина прокрутки

Глубина интерфейса формируется через независимые скорости движения слоёв, создавая иллюзию объёма.

Каждый слой получает собственный коэффициент scroll influence:

const layers = [
  { el: layer1, speed: 0.2 },
  { el: layer2, speed: 0.5 },
  { el: layer3, speed: 1.0 }
];

scrollY$.subscribe((y) => {
  layers.forEach(({ el, speed }) => {
    el.style.transform = `translateY(${y * speed}px)`;
  });
});

Popmotion используется как координирующий слой для таких зависимостей, обеспечивая единый поток значений.