Оптимизация количества сцен

При работе с библиотекой ScrollMagic сцены (scenes) представляют собой основную единицу логики анимации, привязанной к прокрутке страницы. Каждая сцена создаёт свои обработчики событий и наблюдает за положением элемента на странице. При большом количестве сцен производительность страницы может значительно снизиться, поэтому важно понимать методы оптимизации их количества и структуры.


Использование делегирования и общих триггеров

Создание отдельной сцены для каждого анимируемого элемента часто избыточно. Вместо этого можно:

  • Группировать элементы с одинаковой логикой анимации. Если несколько элементов должны анимироваться при достижении одной и той же позиции на странице, можно создать одну сцену и применять анимацию через цикл по элементам.
const controller = new ScrollMagic.Controller();
const items = document.querySelectorAll('.animate');

const scene = new ScrollMagic.Scene({
  triggerElement: '#section',
  duration: '50%'
})
.on('enter', () => {
  items.forEach(item => item.classList.add('visible'));
})
.addTo(controller);
  • Использовать общие триггеры для смежных блоков. Вместо создания отдельных сцен для каждого блока с одинаковым offset достаточно одной сцены с обработкой всех элементов внутри.

Ленивое создание сцен (Lazy Initialization)

Создание всех сцен сразу при загрузке страницы может вызвать задержки рендеринга. Решение — создавать сцены по мере необходимости:

  • Инициализация сцены при прокрутке к определённой точке страницы.
  • Использование IntersectionObserver для отслеживания попадания контейнера в область видимости перед созданием сцены.
const observer = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if (entry.isIntersecting && !entry.target.sceneCreated) {
      new ScrollMagic.Scene({
        triggerElement: entry.target,
        duration: 200
      })
      .setClassToggle(entry.target, 'visible')
      .addTo(controller);
      entry.target.sceneCreated = true;
    }
  });
});

document.querySelectorAll('.lazy-section').forEach(el => observer.observe(el));

Такой подход позволяет уменьшить количество одновременно активных сцен и снижает нагрузку на память.


Использование событий вместо сцены на каждый элемент

Если анимация должна срабатывать только один раз при попадании элемента в область видимости, создание полноценной сцены для каждого элемента может быть излишним. Альтернатива:

  • Подключение обработчиков события scroll или использование requestAnimationFrame.
  • Контроль видимости элементов вручную и вызов анимации при достижении нужной позиции.

Пример с requestAnimationFrame:

const elements = document.querySelectorAll('.fade-in');

function onScroll() {
  elements.forEach(el => {
    const rect = el.getBoundingClientRect();
    if (rect.top < window.innerHeight * 0.8) {
      el.classList.add('visible');
    }
  });
}

window.addEventListener('scroll', () => requestAnimationFrame(onScroll));

Такой метод избавляет от необходимости создавать отдельную сцену для каждого элемента и снижает нагрузку на ScrollMagic.


Контроль длительности и offset сцен

Слишком длинные сцены или сцены с большим количеством offset также влияют на производительность:

  • Минимизировать duration: сцены с анимацией на длительные участки страницы требуют постоянного пересчёта положения элементов.
  • Оптимизировать triggerHook: установка триггера ближе к верхней или нижней границе viewport уменьшает частоту срабатываний, если анимация должна быть краткой.
  • Объединение близких сцен: несколько мелких анимаций, расположенных подряд, можно объединить в одну сцену с комплексным timeline через GSAP.
const tl = gsap.timeline();
tl.from('.item1', {opacity: 0, y: 50})
  .from('.item2', {opacity: 0, y: 50})
  .from('.item3', {opacity: 0, y: 50});

new ScrollMagic.Scene({
  triggerElement: '#container',
  duration: '80%'
})
.setTween(tl)
.addTo(controller);

Отключение неактивных сцен

Если на странице присутствуют сцены, которые не должны работать на определённых устройствах или при определённых условиях:

  • Использовать scene.destroy(true) для удаления сцены и её событий.
  • Создавать сцены условно, проверяя ширину окна или устройство.
if (window.innerWidth > 1024) {
  new ScrollMagic.Scene({
    triggerElement: '#desktop-only',
    duration: 300
  })
  .setClassToggle('#desktop-only', 'active')
  .addTo(controller);
}

Выводы по оптимизации

  • Сокращение количества сцен через объединение и группировку элементов.
  • Ленивая и условная инициализация сцен для снижения нагрузки при старте.
  • Использование timeline и GSAP для нескольких анимаций вместо множества сцен.
  • Контроль длительности, offset и triggerHook для уменьшения вычислений.
  • Удаление или отключение ненужных сцен на мобильных или малопроизводительных устройствах.

Правильная организация и минимизация сцен позволяют поддерживать высокую производительность страницы даже при сложных анимациях, повышая отзывчивость и плавность прокрутки.