Механизм подписок в ScrollMagic строится вокруг событий, генерируемых
объектами Controller и Scene. Каждая сцена
может испускать события, связанные с прокруткой, входом в область
видимости, выходом из неё, прогрессом анимации и обновлением состояния.
Подписки позволяют отслеживать эти изменения и реагировать на них через
обработчики.
Ключевые объекты:
ScrollMagic.Controller — управляет глобальным
состоянием прокруткиScrollMagic.Scene — описывает отдельный участок
прокруткиevents) — точки входа для подписокСцена поддерживает ряд встроенных событий, на которые можно подписываться:
enter — сцена входит в область действияleave — сцена покидает область действияstart — достигнута начальная точка сценыend — достигнута конечная точка сценыprogress — изменяется прогресс (0–1)update — происходит обновление состоянияchange — изменяются параметры сценыshift — смещение позиции сценыПример подписки:
scene.on("enter", function (event) {
console.log("Сцена активирована");
});
Метод .on() используется для регистрации
обработчиков:
scene.on(eventName, callback);
Поддерживается несколько событий одновременно:
scene.on("enter leave", function (event) {
console.log(event.type);
});
Удаление подписки:
scene.off("enter", callback);
Удаление всех подписок:
scene.off("*");
Каждый обработчик получает объект события с полезными свойствами:
type — тип событияtarget — ссылка на сценуprogress — текущий прогресс (для события
progress)state — текущее состояние (BEFORE,
DURING, AFTER)scrollDirection — направление прокрутки
(FORWARD, REVERSE)Пример:
scene.on("progress", function (event) {
console.log(event.progress);
});
Подписки должны управляться явно, особенно в динамических приложениях.
При удалении сцены важно также удалить подписки:
scene.destroy(true);
Параметр true указывает на удаление всех
обработчиков.
Хранение ссылок на функции позволяет корректно удалять подписки:
function onEnter(event) {
console.log("Вход");
}
scene.on("enter", onEnter);
scene.off("enter", onEnter);
Контроллер также генерирует события, например:
updateresizeПример:
controller.on("update", function () {
console.log("Обновление контроллера");
});
По умолчанию this внутри обработчика указывает на объект
сцены:
scene.on("enter", function () {
console.log(this === scene); // true
});
Использование стрелочных функций меняет поведение:
scene.on("enter", () => {
console.log(this); // внешний контекст
});
Частые события, такие как progress и
update, могут вызываться десятки раз в секунду.
let lastCall = 0;
scene.on("progress", function (event) {
const now = Date.now();
if (now - lastCall > 100) {
console.log(event.progress);
lastCall = now;
}
});
let timeout;
scene.on("update", function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
console.log("Обновление завершено");
}, 200);
});
При большом количестве сцен рекомендуется выносить обработчики в отдельные модули:
function attachSceneHandlers(scene) {
scene.on("enter", onEnter);
scene.on("leave", onLeave);
}
Это упрощает поддержку и повторное использование.
Иногда требуется активировать обработчики только при определённых условиях:
scene.on("progress", function (event) {
if (event.progress > 0.5) {
console.log("Больше половины");
}
});
Подписки часто используются совместно с анимационными библиотеками (например, GSAP):
scene.on("enter", function () {
gsap.to(".box", { x: 100 });
});
Для диагностики удобно использовать логирование:
scene.on("*", function (event) {
console.log(event.type);
});
Также полезен режим отладки:
scene.addIndicators();
1. Утечки памяти
Отсутствие удаления подписок при удалении сцены.
2. Дублирование обработчиков
Повторная регистрация одной и той же функции:
scene.on("enter", handler);
scene.on("enter", handler); // вызов дважды
3. Неправильный контекст
Использование стрелочных функций, где нужен this.
const subscriptions = [];
function add(scene, event, handler) {
scene.on(event, handler);
subscriptions.push({ scene, event, handler });
}
function clearAll() {
subscriptions.forEach(({ scene, event, handler }) => {
scene.off(event, handler);
});
}
function createScene(options) {
const scene = new ScrollMagic.Scene(options);
scene.on("enter", () => console.log("enter"));
scene.on("leave", () => console.log("leave"));
return scene;
}
const config = [
{ event: "enter", handler: onEnter },
{ event: "leave", handler: onLeave }
];
config.forEach(({ event, handler }) => {
scene.on(event, handler);
});
В одностраничных приложениях важно очищать подписки при смене маршрутов:
function cleanup() {
scene.destroy(true);
controller.destroy(true);
}
Инициализация при монтировании:
function init() {
controller = new ScrollMagic.Controller();
scene = new ScrollMagic.Scene({ duration: 300 });
}
Подписки могут использовать асинхронный код:
scene.on("enter", async function () {
const data = await fetch("/api/data");
console.log(await data.json());
});
Можно создавать собственные события:
scene.trigger("customEvent", { value: 42 });
scene.on("customEvent", function (event) {
console.log(event.value);
});
const controller = new ScrollMagic.Controller({
refreshInterval: 100
});
Это уменьшает количество событий update.
Часто обработчики работают с DOM:
scene.on("enter", function () {
document.querySelector(".box").classList.add("active");
});
Важно избегать лишних операций:
Создание классов для управления сценами:
class SceneManager {
constructor(scene) {
this.scene = scene;
this.init();
}
init() {
this.scene.on("enter", this.onEnter.bind(this));
}
onEnter() {
console.log("enter");
}
destroy() {
this.scene.off("enter", this.onEnter);
}
}
Подписки могут зависеть друг от друга:
scene1.on("enter", function () {
scene2.enabled(true);
});