Scroll triggers

Библиотека Motion One предоставляет низкоуровневый и при этом выразительный API для анимаций в браузере, где ключевым строительным блоком выступает реактивность к прокрутке страницы. Работа со скроллом в Motion One строится вокруг концепции привязки анимации к положению документа или элемента относительно viewport, что позволяет реализовывать как простые эффекты появления, так и сложные сценические переходы с синхронизацией движения.

Основной инструмент для работы с прокруткой — функция scroll, которая связывает прогресс прокрутки с изменением свойств анимации. В отличие от классических событий scroll, где требуется вручную вычислять позиции элементов, Motion One абстрагирует расчёты и предоставляет нормализованное значение прогресса.

В основе механизма лежит преобразование скролла в числовую шкалу. Эта шкала используется как управляющий параметр для анимации.

p =

Где p — нормализованный прогресс, ограниченный диапазоном от 0 до 1. Этот подход позволяет унифицировать поведение анимаций вне зависимости от размеров экрана или контента.

Функция scroll в Motion One принимает коллбэк, который получает обновления этого прогресса:

import { scroll, animate } from "motion";

scroll(({ y }) => {
  // y прогресс прокрутки по вертикали (0 → 1)
});

Значение y отражает относительное положение прокрутки страницы. Аналогично доступно горизонтальное направление x, если используется горизонтальный контейнер.

Привязка анимации к скроллу

Одним из базовых сценариев является прямое связывание прогресса прокрутки с CSS-свойствами элемента.

scroll(({ y }) => {
  animate(".box", {
    opacity: y,
    transform: `translateY(${(1 - y) * 50}px)`
  });
});

В данном случае анимация полностью зависит от положения страницы: элемент становится прозрачнее и смещается по мере прокрутки.

Такой подход подходит для простых эффектов, однако Motion One позволяет более тонко управлять диапазонами активации.

Диапазоны и триггерные области

Скролл-анимации редко должны зависеть от всей страницы. Чаще требуется запуск в пределах конкретного сегмента документа. Для этого используются offsets — параметры, задающие границы начала и конца анимации.

Типичная модель выглядит как интервал [start, end], где значения могут быть заданы в процентах, пикселях или относительных единицах.

scroll(
  ({ y }) => {
    animate(".panel", {
      scale: y
    });
  },
  {
    offset: ["start end", "end start"]
  }
);

Такая конфигурация задаёт момент начала анимации при появлении элемента внизу viewport и завершение при его полном выходе вверх.

Принцип триггеров появления

Scroll triggers в Motion One часто реализуются через сочетание прогресса и порогов активации. Это позволяет имитировать поведение IntersectionObserver, но с более плавной и управляемой интерполяцией.

trigger =

Где t — порог срабатывания. Однако в Motion One вместо дискретного переключения используется интерполяция, что делает переходы визуально мягкими.

Связывание нескольких свойств с одним прогрессом

Одним из сильных аспектов подхода Motion One является возможность одновременно управлять несколькими CSS-свойствами через один scroll-источник.

scroll(({ y }) => {
  animate(".card", {
    opacity: y,
    transform: `
      translateY(${(1 - y) * 40}px)
      rotate(${(1 - y) * 5}deg)
    `
  });
});

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

Scroll-driven timeline

При усложнении логики анимации используется концепция временной шкалы, где скролл становится заменой времени. В этом режиме прокрутка выступает как аналог временной оси.

t_{animation} = f(p_{scroll})

Функция f определяет нелинейное преобразование, например easing, ускорение или задержку.

scroll(({ y }) => {
  const eased = y * y;

  animate(".scene", {
    transform: `translateX(${eased * 200}px)`
  });
});

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

Управление несколькими секциями страницы

Сложные интерфейсы часто состоят из нескольких независимых зон скролла. Motion One поддерживает локальные контейнеры, где прокрутка отслеживается относительно элемента, а не всего документа.

scroll(
  ({ y }) => {
    animate(".section", {
      scale: y
    });
  },
  {
    target: document.querySelector(".container")
  }
);

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

Привязка к видимости элемента

Scroll triggers часто комбинируются с логикой появления элемента в viewport. Это создаёт гибридную модель: элемент не только отслеживает прокрутку, но и активируется только при достижении области видимости.

scroll(({ y }) => {
  const element = document.querySelector(".feature");

  if (y > 0.2 && y < 0.8) {
    animate(element, {
      opacity: 1,
      transform: "translateY(0px)"
    });
  } else {
    animate(element, {
      opacity: 0,
      transform: "translateY(20px)"
    });
  }
});

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

Скролл как управляющая ось параллакса

Параллакс-эффекты — один из наиболее распространённых сценариев scroll triggers. Motion One позволяет создавать многослойное движение, где каждый слой имеет собственную скорость относительно прогресса.

scroll(({ y }) => {
  animate(".background", {
    transform: `translateY(${y * 100}px)`
  });

  animate(".foreground", {
    transform: `translateY(${y * 30}px)`
  });
});

Разница коэффициентов создаёт иллюзию глубины пространства.

Нормализация поведения и производительность

Ключевая особенность Motion One заключается в оптимизации обновлений. Вместо прямого реагирования на каждый scroll-event используется батчинг и синхронизация с requestAnimationFrame. Это уменьшает количество перерасчётов layout и повышает плавность анимации.

Поток обработки можно представить следующим образом:

scrollEvent normalize(p) frameUpdate animate()

Такая цепочка обеспечивает стабильное поведение даже при высокой частоте прокрутки.

Комбинирование scroll и in-view логики

В более сложных интерфейсах scroll triggers часто дополняются проверкой пересечения элемента с viewport. Это позволяет запускать анимацию только при необходимости, избегая лишних вычислений.

import { scroll, animate } from "motion";

scroll(({ y }) => {
  document.querySelectorAll(".item").forEach((item) => {
    const rect = item.getBoundingClientRect();

    const visible = rect.top < window.innerHeight && rect.bottom > 0;

    if (visible) {
      animate(item, {
        opacity: y
      });
    }
  });
});

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

Динамическая привязка к координатам

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

scroll(({ y }) => {
  const cards = document.querySelectorAll(".card");

  cards.forEach((card, index) => {
    animate(card, {
      transform: `translateY(${y * (index * 10)}px)`
    });
  });
});

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

Интерполяция значений и плавность переходов

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

y’ = y^k, k > 1

При увеличении степени k движение становится более замедленным в начале и ускоренным в конце, что часто используется для эффектов появления.

scroll(({ y }) => {
  const smooth = Math.pow(y, 1.8);

  animate(".title", {
    transform: `translateY(${(1 - smooth) * 60}px)`
  });
});

Управление состоянием через scroll progress

Scroll triggers могут использоваться не только для визуальных эффектов, но и для управления логикой интерфейса. Прогресс прокрутки становится источником состояния.

scroll(({ y }) => {
  const state = y > 0.5 ? "active" : "idle";

  document.body.dataset.state = state;
});

Такой подход позволяет синхронизировать CSS и JavaScript без дополнительных событий.

Композиция нескольких scroll-источников

В сложных интерфейсах часто присутствует несколько независимых scroll-источников: основной документ, модальные окна, горизонтальные секции. Motion One допускает параллельное использование нескольких scroll-наблюдателей, каждый из которых управляет своей областью анимации.

scroll(({ y }) => {
  animate(".main", { opacity: y });
});

scroll(
  ({ y }) => {
    animate(".modal", { scale: y });
  },
  { target: ".modal-container" }
);

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

Поведение при резких скачках прокрутки

При программном изменении scroll-позиции (например, через scrollTo) Motion One сохраняет непрерывность прогресса, интерполируя переходы между значениями. Это предотвращает резкие визуальные разрывы и поддерживает целостность анимационного состояния.

Математически это можно представить как сглаживание сигнала:

p_{smoothed}(t) = p(t) + (1 - )p(t-1)

Где коэффициент сглаживания регулирует инерционность переходов.