ScrollMagic предоставляет низкоуровневый API для управления сценами прокрутки, однако в реальных проектах часто возникает необходимость в более удобных и декларативных подходах. Для этого применяются обертки — дополнительные слои абстракции, упрощающие создание и управление сценами.
Назначение оберток:
class ScrollScene {
constructor({ trigger, duration = 0, offset = 0, hook = 0.5 }) {
this.scene = new ScrollMagic.Scene({
triggerElement: trigger,
duration,
offset,
triggerHook: hook
});
}
addTo(controller) {
this.scene.addTo(controller);
return this;
}
setTween(tween) {
this.scene.setTween(tween);
return this;
}
on(event, callback) {
this.scene.on(event, callback);
return this;
}
}
Преимущества:
Вместо создания контроллера в каждом модуле используется единая точка управления:
class ScrollManager {
constructor() {
this.controller = new ScrollMagic.Controller();
this.scenes = [];
}
addScene(scene) {
scene.addTo(this.controller);
this.scenes.push(scene);
}
destroy() {
this.scenes.forEach(scene => scene.scene.destroy(true));
this.controller.destroy(true);
}
}
const scrollManager = new ScrollManager();
Ключевые моменты:
Фабричный подход позволяет быстро создавать типовые сцены.
function createFadeScene(selector) {
return new ScrollScene({
trigger: selector,
duration: 200
}).setTween(
gsap.from(selector, { opacity: 0, y: 50 })
);
}
Использование:
scrollManager.addScene(createFadeScene('.block'));
Преимущества фабрик:
При работе с большим количеством сцен полезно объединять их в группы.
class SceneGroup {
constructor() {
this.scenes = [];
}
add(scene) {
this.scenes.push(scene);
return this;
}
addTo(controller) {
this.scenes.forEach(scene => scene.addTo(controller));
}
destroy() {
this.scenes.forEach(scene => scene.scene.destroy(true));
}
}
Применение:
ScrollMagic не предоставляет встроенных инструментов для поиска элементов, поэтому часто используются вспомогательные функции.
function selectAll(selector) {
return Array.from(document.querySelectorAll(selector));
}
function createScenesForElements(selector, factory) {
return selectAll(selector).map(el => factory(el));
}
Использование:
const scenes = createScenesForElements('.item', el =>
createFadeScene(el)
);
scenes.forEach(scene => scrollManager.addScene(scene));
При обработке событий прокрутки важно ограничивать частоту вызовов.
function debounce(fn, delay) {
let timeout;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => fn.apply(this, arguments), delay);
};
}
Применение:
window.addEventListener('resize', debounce(() => {
scrollManager.controller.update(true);
}, 200));
Зачем это нужно:
ScrollMagic сцены часто требуют пересчета при изменении размеров экрана.
function isMobile() {
return window.innerWidth <= 768;
}
Пример адаптации:
const scene = new ScrollScene({
trigger: '.section',
duration: isMobile() ? 100 : 300
});
Создание сцен только при необходимости уменьшает нагрузку.
function lazyScene(selector, factory) {
let initialized = false;
return () => {
if (!initialized) {
initialized = true;
return factory(selector);
}
};
}
ScrollMagic часто используется вместе с GSAP, поэтому создаются вспомогательные функции:
function animateOnScroll(selector, animation) {
return new ScrollScene({
trigger: selector,
duration: 300
}).setTween(animation);
}
Пример:
scrollManager.addScene(
animateOnScroll('.box', gsap.to('.box', { x: 200 }))
);
Повторные запросы к DOM — дорогостоящая операция.
const elementCache = new Map();
function getElement(selector) {
if (!elementCache.has(selector)) {
elementCache.set(selector, document.querySelector(selector));
}
return elementCache.get(selector);
}
Создание утилиты для логирования событий сцен:
function debugScene(scene, name) {
scene.on('enter', () => console.log(`${name}: enter`));
scene.on('leave', () => console.log(`${name}: leave`));
}
ScrollMagic поддерживает расширения, которые можно оборачивать в удобные интерфейсы.
Пример — индикаторы:
function addIndicators(scene) {
scene.scene.addIndicators({
colorStart: 'green',
colorEnd: 'red'
});
}
Сильная сторона архитектуры — возможность комбинировать обертки:
const scene = createFadeScene('.card');
debugScene(scene, 'Card Animation');
scrollManager.addScene(scene);
Разделение ответственности:
Повторное использование:
Модульность:
const config = [
{ selector: '.a', animation: { opacity: 0 } },
{ selector: '.b', animation: { x: 100 } }
];
config.forEach(item => {
scrollManager.addScene(
animateOnScroll(
item.selector,
gsap.from(item.selector, item.animation)
)
);
});
<div class="anim" data-duration="200"></div>
selectAll('.anim').forEach(el => {
const duration = el.dataset.duration;
scrollManager.addScene(
new ScrollScene({
trigger: el,
duration
})
);
});
При использовании фреймворков важно корректно уничтожать сцены:
function cleanup() {
scrollManager.destroy();
}
Обертки и утилиты превращают ScrollMagic из низкоуровневого инструмента в гибкую архитектурную систему. Правильная организация кода позволяет: