Процедурная генерация анимаций в контексте 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() — ограниченияЛинейная интерполяция — основа процедурных анимаций.
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.
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);
});
Преимущества:
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);
Особенность:
Процедурная генерация требует контроля состояния.
let active = false;
.on("enter", () => active = true)
.on("leave", () => active = false)
.on("progress", (event) => {
if (!active) return;
element.style.opacity = event.progress;
});
Зачем это нужно:
При процедурной генерации важно учитывать нагрузку:
Рекомендации:
requestAnimationFrame при сложных
вычисленияхprogressconst el = document.querySelector(".box");
.on("progress", (event) => {
const p = event.progress;
const transform = `translateX(${p * 300}px)`;
el.style.transform = transform;
});
Процедурная система легко масштабируется:
function animate(el, progress, config) {
const { x, y } = config;
el.style.transform = `
translate(${x(progress)}px, ${y(progress)}px)
`;
}
Процедурная генерация лучше всего работает при разделении:
const config = {
x: (t) => t * 400,
y: (t) => Math.sin(t * Math.PI) * 100
};
.on("progress", (event) => {
animate(element, event.progress, config);
});
Такой подход превращает анимации из статического набора эффектов в гибкую систему, управляемую алгоритмами и данными.