Производительность при resize

Работа с событиями изменения размера окна (resize) в браузере является одной из ключевых тем при использовании ScrollMagic. Из-за того, что браузер может генерировать десятки и сотни событий resize при одном изменении размера окна, без оптимизации производительность страницы существенно падает, особенно если сцены ScrollMagic пересчитываются на каждом событии.


Пересчет размеров и ScrollMagic

ScrollMagic опирается на расчёт размеров контейнеров и элементов для корректного определения триггеров и продолжительности сцен. Ключевые методы:

  • scene.update() – пересчитывает позицию сцены относительно текущего скролла и размеров.
  • controller.update() – обновляет все сцены в контроллере.

При каждом событии resize вызывается пересчет, что может привести к серьёзной нагрузке, если сцен много или на странице сложная анимация.

window.addEventListener("resize", function() {
    controller.update();
});

Этот подход корректен, но создаёт проблему: множество лишних вызовов, которые тормозят рендеринг.


Использование дебаунса (debounce)

Для оптимизации применяют дебаунсинг – ограничение частоты вызова функции до одного раза за определённый промежуток времени. В JavaScript это реализуется через setTimeout:

let resizeTimeout;

window.addEventListener("resize", function() {
    clearTimeout(resizeTimeout);
    resizeTimeout = setTimeout(function() {
        controller.update();
    }, 200);
});

Пояснение:

  • clearTimeout отменяет предыдущий вызов.
  • Новая функция controller.update() будет выполнена только после 200 мс без новых событий resize.
  • Значение задержки подбирается эмпирически: для сложных анимаций лучше увеличить до 300–400 мс.

Этот подход значительно снижает нагрузку на браузер, сохраняя корректность расчётов сцен.


Оптимизация с requestAnimationFrame

Иногда применяют requestAnimationFrame, чтобы синхронизировать пересчёт с циклом рендеринга браузера. Это особенно полезно, если требуется плавная реакция на resize без лишних просадок FPS.

let resizePending = false;

window.addEventListener("resize", function() {
    if (!resizePending) {
        resizePending = true;
        requestAnimationFrame(function() {
            controller.update();
            resizePending = false;
        });
    }
});

Преимущества:

  • Сцены обновляются только один раз за кадр.
  • Поддерживается плавность анимаций.
  • Снижается нагрузка на основной поток браузера.

Оптимизация сцен ScrollMagic

Для больших проектов полезно минимизировать работу ScrollMagic сцен на resize:

  1. Группировка сцен по контейнерам: пересчитывать только те сцены, которые зависят от конкретного блока.
function updateSectionScenes(sectionController) {
    sectionController.update();
}

window.addEventListener("resize", function() {
    clearTimeout(resizeTimeout);
    resizeTimeout = setTimeout(function() {
        updateSectionScenes(controllerSection1);
        updateSectionScenes(controllerSection2);
    }, 250);
});
  1. Использование scene.refresh() – пересчитывает размеры и позиции сцены с учётом изменившихся DOM элементов:
scene.refresh();

Это особенно важно при динамическом контенте, который может менять высоту блоков.


Избегание лишних пересчетов

  • Не вызывать controller.update() на каждом scroll или resize, если это не критично для сцены.
  • Для сцен с фиксированной высотой или без зависимостей от размеров окна можно вообще исключить вызов обновления.
  • Комбинировать debounce и requestAnimationFrame для максимальной производительности.

Пример комплексной оптимизации

let resizeTimeout;
let resizePending = false;

function optimizedResizeUpdate() {
    if (!resizePending) {
        resizePending = true;
        requestAnimationFrame(function() {
            scenes.forEach(scene => scene.refresh());
            resizePending = false;
        });
    }
}

window.addEventListener("resize", function() {
    clearTimeout(resizeTimeout);
    resizeTimeout = setTimeout(optimizedResizeUpdate, 200);
});

Особенности подхода:

  • Каждое событие resize сначала обрабатывается через debounce.
  • Внутри debounce используется requestAnimationFrame.
  • Обновляются только нужные сцены методом refresh().
  • Поддерживается плавная работа ScrollMagic без лишней нагрузки.

Рекомендации по производительности

  • Использовать debounce для всех событий resize.
  • Применять requestAnimationFrame для синхронизации обновлений с рендером.
  • Минимизировать количество сцен, пересчитываемых на resize.
  • Для динамического контента вызывать scene.refresh() только при реальной необходимости.
  • Измерять производительность через инструменты DevTools, особенно при сложных анимациях и большом количестве сцен.

Эффективная работа ScrollMagic при изменении размера окна напрямую зависит от грамотного управления обновлением сцен и контроллера, а сочетание debounce с requestAnimationFrame обеспечивает высокую производительность даже на сложных страницах.