Библиотеки плавной прокрутки в JavaScript, включая популярные решения вроде Smooth Scroll, часто строятся с возможностью расширения через плагины. Это позволяет внедрять дополнительную логику без изменения исходного кода библиотеки.
Основой такой архитектуры является инверсия управления (IoC): ядро библиотеки предоставляет точки расширения, а пользователь подключает собственные обработчики, которые внедряются в жизненный цикл прокрутки.
Ключевые элементы:
Smooth Scroll обычно предоставляет набор событий, на которые можно подписаться:
scrollStartscrollEndscrollCancelbeforeScrollafterScrollСоздание собственного плагина начинается с перехвата этих событий:
const scroll = new SmoothScroll('a[href*="#"]');
scroll.on('scrollStart', (anchor, toggle) => {
console.log('Начало прокрутки к', anchor.id);
});
Плагин может:
Собственный плагин оформляется как функция, принимающая экземпляр Smooth Scroll:
function CustomPlugin(instance) {
instance.on('scrollStart', (anchor) => {
// логика
});
instance.on('scrollEnd', (anchor) => {
// логика
});
}
Подключение:
const scroll = new SmoothScroll('a[href*="#"]');
CustomPlugin(scroll);
Некоторые реализации позволяют внедрять промежуточные функции (middleware), которые изменяют поведение до выполнения основной логики.
Пример:
function SpeedModifier(instance) {
const originalAnimate = instance.animateScroll;
instance.animateScroll = function(anchor, toggle, options) {
options.speed = options.speed * 2; // ускорение
return originalAnimate.call(this, anchor, toggle, options);
};
}
Этот подход:
Плагины могут добавлять собственные параметры конфигурации:
function OffsetPlugin(instance, config = {}) {
const offset = config.offset || 0;
instance.on('beforeScroll', (anchor) => {
window.scrollBy(0, -offset);
});
}
Использование:
OffsetPlugin(scroll, { offset: 100 });
Важно:
Сложные плагины требуют хранения состояния:
function ScrollTracker(instance) {
let lastPosition = 0;
instance.on('scrollStart', () => {
lastPosition = window.pageYOffset;
});
instance.on('scrollEnd', () => {
const newPosition = window.pageYOffset;
console.log('Прокрутка:', newPosition - lastPosition);
});
}
Рекомендации:
Пример плагина, добавляющего активный класс текущему якорю:
function ActiveLinkPlugin(instance) {
const links = document.querySelectorAll('a[href*="#"]');
instance.on('scrollStart', (anchor) => {
links.forEach(link => link.classList.remove('active'));
const activeLink = document.querySelector(`a[href="#${anchor.id}"]`);
if (activeLink) {
activeLink.classList.add('active');
}
});
}
Иногда требуется изменить базовую механику:
function CustomEasingPlugin(instance) {
instance.easingPattern = function(time) {
return time < 0.5
? 2 * time * time
: 1 - Math.pow(-2 * time + 2, 2) / 2;
};
}
Такой подход влияет на:
Плагины можно комбинировать:
const scroll = new SmoothScroll('a[href*="#"]');
ActiveLinkPlugin(scroll);
ScrollTracker(scroll);
SpeedModifier(scroll);
Проблемы:
Решение:
Хороший плагин:
Пример безопасной проверки:
if (!instance._customPluginInitialized) {
instance._customPluginInitialized = true;
}
Иногда необходимо дождаться полной загрузки DOM:
function LazyInitPlugin(instance) {
window.addEventListener('load', () => {
instance.on('scrollEnd', () => {
console.log('DOM полностью загружен');
});
});
}
Плагин может выполнять асинхронные операции:
function AnalyticsPlugin(instance) {
instance.on('scrollEnd', async (anchor) => {
await fetch('/analytics', {
method: 'POST',
body: JSON.stringify({ section: anchor.id })
});
});
}
Особенности:
Для разработки полезно внедрить логирование:
function DebugPlugin(instance) {
instance.on('scrollStart', (anchor) => {
console.log('[DEBUG] Start:', anchor);
});
instance.on('scrollEnd', (anchor) => {
console.log('[DEBUG] End:', anchor);
});
}
Проверяется:
Минимальный тест:
const mockInstance = {
events: {},
on(event, handler) {
this.events[event] = handler;
}
};
CustomPlugin(mockInstance);
plugin/
├── index.js
├── config.js
├── handlers/
│ ├── scrollStart.js
│ └── scrollEnd.js
└── utils/
└── helpers.js
Это упрощает:
Часто используются:
Такая модель обеспечивает гибкость и позволяет строить сложные системы поведения поверх базовой прокрутки без вмешательства в ядро библиотеки.