Библиотека ScrollMagic изначально спроектирована с учётом
расширяемости. Её ядро предоставляет базовые сущности —
Controller, Scene и механизм событий — а
дополнительные возможности реализуются через плагины. Такой подход
позволяет добавлять функциональность без изменения исходного кода
библиотеки.
Плагин в ScrollMagic — это модуль, который расширяет поведение
Scene или Controller, добавляя новые методы,
свойства или перехватывая события жизненного цикла.
Независимость Плагин должен быть изолирован и не зависеть от внутренней реализации ядра, кроме официально доступных API.
Расширение прототипов Чаще всего плагины
добавляют методы в ScrollMagic.Scene.prototype или
ScrollMagic.Controller.prototype.
Использование событий Плагины взаимодействуют с
системой через события (enter, leave,
progress, update).
Идемпотентность Повторное подключение плагина не должно ломать поведение.
Минимальный шаблон плагина:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['scrollmagic'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('scrollmagic'));
} else {
factory(root.ScrollMagic);
}
}(this, function (ScrollMagic) {
if (!ScrollMagic) {
throw new Error('ScrollMagic is required');
}
ScrollMagic.Scene.prototype.myPluginMethod = function () {
// логика плагина
return this;
};
}));
Ключевые моменты:
Scenethis для цепочки вызововПростейший способ расширения:
ScrollMagic.Scene.prototype.setBackgroundColor = function (color) {
this.on("enter", function () {
document.body.style.backgroundColor = color;
});
return this;
};
Использование:
new ScrollMagic.Scene({ triggerElement: "#trigger" })
.setBackgroundColor("black")
.addTo(controller);
ScrollMagic активно использует событийную модель. Плагин может подписываться на события:
startendenterleaveprogressupdateПример:
ScrollMagic.Scene.prototype.logProgress = function () {
this.on("progress", function (event) {
console.log("Progress:", event.progress);
});
return this;
};
Иногда требуется изменить поведение уже существующих методов. Для этого используется обёртка:
const originalAddTo = ScrollMagic.Scene.prototype.addTo;
ScrollMagic.Scene.prototype.addTo = function (controller) {
console.log("Scene добавляется в controller");
return originalAddTo.call(this, controller);
};
Важно: Всегда сохранять оригинальную функцию и
вызывать её через call.
Плагин может принимать параметры:
ScrollMagic.Scene.prototype.highlight = function (options) {
const settings = Object.assign({
color: "yellow",
duration: 0.3
}, options);
this.on("enter", function () {
this.triggerElement().style.transition = `background ${settings.duration}s`;
this.triggerElement().style.background = settings.color;
});
this.on("leave", function () {
this.triggerElement().style.background = "";
});
return this;
};
Использование:
scene.highlight({ color: "red", duration: 0.5 });
Плагины могут добавлять функциональность и контроллеру:
ScrollMagic.Controller.prototype.getScenesCount = function () {
return this.info("size");
};
Для сложной логики требуется хранение состояния:
ScrollMagic.Scene.prototype.counter = function () {
let count = 0;
this.on("enter", function () {
count++;
console.log("Входов:", count);
});
return this;
};
Для хранения внутренних данных удобно использовать замыкания:
ScrollMagic.Scene.prototype.timer = function () {
let startTime = null;
this.on("enter", function () {
startTime = Date.now();
});
this.on("leave", function () {
const duration = Date.now() - startTime;
console.log("Время в зоне:", duration);
});
return this;
};
ScrollMagic часто используется вместе с анимационными библиотеками. Плагин может выступать адаптером:
ScrollMagic.Scene.prototype.animateCSS = function (className) {
this.on("enter", function () {
this.triggerElement().classList.add(className);
});
this.on("leave", function () {
this.triggerElement().classList.remove(className);
});
return this;
};
Жизненный цикл сцены:
Плагин может реагировать на уничтожение:
ScrollMagic.Scene.prototype.cleanup = function () {
this.on("destroy", function () {
console.log("Scene уничтожена");
});
return this;
};
Для удобства API все методы должны возвращать this:
scene
.highlight()
.logProgress()
.timer();
Плагин должен проверять входные данные:
ScrollMagic.Scene.prototype.safeColor = function (color) {
if (typeof color !== "string") {
throw new Error("Color должен быть строкой");
}
return this;
};
Чтобы избежать конфликтов имён:
sm_, plugin_)if (!ScrollMagic.Scene.prototype.myPlugin) {
ScrollMagic.Scene.prototype.myPlugin = function () {};
}
Для крупных плагинов используется разбиение:
plugin/
├── index.js
├── core.js
├── events.js
└── utils.js
При разработке плагинов важно учитывать:
progressПлохой пример:
this.on("progress", function () {
document.querySelectorAll(".item");
});
Хороший пример:
const items = document.querySelectorAll(".item");
this.on("progress", function () {
// работа с уже найденными элементами
});
Проверяются:
Каждый метод должен иметь описание:
/**
* Подсветка элемента при входе в viewport
* @param {Object} options
* @param {string} options.color
* @param {number} options.duration
*/
1. Комбинирование плагинов
scene
.highlight({ color: "blue" })
.timer()
.logProgress();
2. Декораторы
Плагин может изменять поведение других методов.
3. Lazy-инициализация
let initialized = false;
this.on("enter", function () {
if (!initialized) {
initialized = true;
// инициализация
}
});
ScrollMagic.Scene.prototype.pinCustom = function () {
let element = this.triggerElement();
this.on("enter", function () {
element.style.position = "fixed";
element.style.top = "0";
});
this.on("leave", function () {
element.style.position = "";
element.style.top = "";
});
return this;
};
<script src="ScrollMagic.min.js"></script>
<script src="plugin.js"></script>
или через сборщик:
import ScrollMagic from "scrollmagic";
import "./plugin";
Плагин оформляется как npm-пакет:
package.jsonВажно учитывать:
Вместо изменения ядра — создание надстроек:
function createAdvancedScene(options) {
return new ScrollMagic.Scene(options)
.highlight()
.timer();
}
Если плагин требует внешнюю библиотеку:
if (!window.SomeLibrary) {
throw new Error("SomeLibrary required");
}
При росте проекта:
При уничтожении сцены необходимо очищать:
this.on("destroy", function () {
clearInterval(timer);
});