Процедурная генерация анимаций

Процедурная генерация анимаций в контексте ScrollMagic — это создание динамических эффектов не через статические таймлайны, а с помощью алгоритмов, которые рассчитывают параметры анимации на основе прокрутки, состояния DOM и пользовательских данных. Такой подход позволяет:

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

ScrollMagic предоставляет базовые инструменты (сцены, контроллеры, триггеры), а процедурная логика строится поверх них.


Связь прокрутки с параметрами анимации

Ключевой механизм — преобразование позиции скролла в числовые значения, которые управляют стилями или анимациями.

const controller = new ScrollMagic.Controller();

new ScrollMagic.Scene({
  triggerElement: ".section",
  duration: 500
})
.on("progress", function (event) {
  const progress = event.progress; // от 0 до 1

  const scale = 1 + progress;
  const opacity = 1 - progress;

  document.querySelector(".box").style.transform = `scale(${scale})`;
  document.querySelector(".box").style.opacity = opacity;
})
.addTo(controller);

Особенности:

  • progress — основной параметр процедурной генерации
  • значения интерполируются вручную
  • нет привязки к конкретной библиотеке анимации

Генерация множества анимаций

Процедурный подход особенно эффективен при работе с коллекциями элементов.

const items = document.querySelectorAll(".item");

items.forEach((item, index) => {
  new ScrollMagic.Scene({
    triggerElement: item,
    triggerHook: 0.8,
    duration: 300
  })
  .on("progress", (event) => {
    const delay = index * 0.1;
    const adjusted = Math.max(0, event.progress - delay);

    item.style.transform = `translateY(${50 - adjusted * 50}px)`;
    item.style.opacity = adjusted;
  })
  .addTo(controller);
});

Что происходит:

  • каждая анимация рассчитывается на основе индекса
  • создаётся эффект каскада без ручной настройки
  • параметры масштабируются автоматически

Использование математических функций

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

.on("progress", (event) => {
  const p = event.progress;

  const wave = Math.sin(p * Math.PI);
  const rotate = wave * 45;

  element.style.transform = `rotate(${rotate}deg)`;
});

Типичные функции:

  • Math.sin() — волновые эффекты
  • Math.cos() — фазовые смещения
  • Math.pow() — ускорение/замедление
  • Math.min()/Math.max() — ограничения

Интерполяция значений (lerp)

Линейная интерполяция — основа процедурных анимаций.

function lerp(start, end, t) {
  return start + (end - start) * t;
}

.on("progress", (event) => {
  const x = lerp(0, 300, event.progress);
  const opacity = lerp(1, 0, event.progress);

  element.style.transform = `translateX(${x}px)`;
  element.style.opacity = opacity;
});

Преимущества:

  • универсальность
  • предсказуемость
  • легко комбинируется

Нелинейные кривые (easing)

Для более естественного движения применяются функции easing.

function easeInOut(t) {
  return t < 0.5
    ? 2 * t * t
    : 1 - Math.pow(-2 * t + 2, 2) / 2;
}

.on("progress", (event) => {
  const eased = easeInOut(event.progress);

  element.style.transform = `scale(${1 + eased})`;
});

Эффект:

  • плавное ускорение и замедление
  • устранение «механичности»

Генерация параметров на основе данных

Анимации могут зависеть от внешних данных:

const data = [10, 50, 30, 80];

data.forEach((value, index) => {
  const el = document.createElement("div");
  el.classList.add("bar");
  document.body.appendChild(el);

  new ScrollMagic.Scene({
    triggerElement: el,
    duration: 400
  })
  .on("progress", (event) => {
    const height = value * event.progress;
    el.style.height = `${height}px`;
  })
  .addTo(controller);
});

Результат:

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

Комбинирование нескольких параметров

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

.on("progress", (event) => {
  const p = event.progress;

  const x = p * 200;
  const y = Math.sin(p * Math.PI) * 50;
  const opacity = p;

  element.style.transform = `translate(${x}px, ${y}px)`;
  element.style.opacity = opacity;
});

Комбинации:

  • позиция + прозрачность
  • масштаб + вращение
  • движение по траектории

Генерация траекторий

Движение можно задавать не линейно, а по кривым.

.on("progress", (event) => {
  const t = event.progress;

  const x = 300 * t;
  const y = 100 * Math.sin(t * Math.PI);

  element.style.transform = `translate(${x}px, ${y}px)`;
});

Типы траекторий:

  • синусоидальные
  • параболические
  • спиральные
  • пользовательские

Динамическое создание сцен

Процедурный подход позволяет генерировать сцены на лету.

function createScene(selector, duration) {
  return new ScrollMagic.Scene({
    triggerElement: selector,
    duration: duration
  })
  .on("progress", (event) => {
    const el = document.querySelector(selector);
    el.style.opacity = event.progress;
  })
  .addTo(controller);
}

[".a", ".b", ".c"].forEach((cls, i) => {
  createScene(cls, 200 + i * 100);
});

Преимущества:

  • переиспользуемость
  • централизованная логика
  • уменьшение дублирования

Связь с GSAP для процедурной логики

ScrollMagic может использоваться вместе с GSAP, но логика остаётся процедурной.

const tween = gsap.to(".box", {
  x: 500,
  paused: true
});

new ScrollMagic.Scene({
  duration: 400
})
.on("progress", (event) => {
  tween.progress(event.progress);
})
.addTo(controller);

Особенность:

  • ScrollMagic управляет временем
  • GSAP — визуализацией

Управление состоянием

Процедурная генерация требует контроля состояния.

let active = false;

.on("enter", () => active = true)
.on("leave", () => active = false)
.on("progress", (event) => {
  if (!active) return;

  element.style.opacity = event.progress;
});

Зачем это нужно:

  • оптимизация
  • предотвращение лишних вычислений
  • контроль жизненного цикла

Производительность

При процедурной генерации важно учитывать нагрузку:

Рекомендации:

  • использовать requestAnimationFrame при сложных вычислениях
  • минимизировать доступ к DOM
  • кэшировать элементы
  • избегать тяжёлых вычислений внутри progress
const el = document.querySelector(".box");

.on("progress", (event) => {
  const p = event.progress;
  const transform = `translateX(${p * 300}px)`;

  el.style.transform = transform;
});

Расширяемость

Процедурная система легко масштабируется:

  • добавление новых параметров
  • изменение формул
  • внедрение пользовательских функций
  • интеграция с API
function animate(el, progress, config) {
  const { x, y } = config;

  el.style.transform = `
    translate(${x(progress)}px, ${y(progress)}px)
  `;
}

Архитектурный подход

Процедурная генерация лучше всего работает при разделении:

  • данные (конфигурации)
  • логика (функции)
  • рендеринг (DOM/GSAP)
const config = {
  x: (t) => t * 400,
  y: (t) => Math.sin(t * Math.PI) * 100
};

.on("progress", (event) => {
  animate(element, event.progress, config);
});

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