Code splitting

Code Splitting — это методика разделения кода на отдельные чанки, которые загружаются по мере необходимости. В контексте ScrollMagic это особенно важно, так как библиотеки для анимаций и управления скроллом часто весят немало и могут замедлять первичную загрузку страницы.

ScrollMagic сам по себе небольшой, но его плагины и зависимости (GSAP, анимации на CSS, контроллеры и сцены) могут существенно увеличивать размер бандла. Использование code splitting позволяет загружать основные скрипты сразу, а сложные анимации — только тогда, когда они становятся актуальными.


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

В ScrollMagic ключевым объектом является Controller, который управляет всеми сценами (Scene). Каждая сцена может быть отдельной анимацией, привязанной к скроллу.

Пример структуры с code splitting:

// main.js
import ScrollMagic from "scrollmagic";

const controller = new ScrollMagic.Controller();

document.addEventListener("DOMContentLoaded", async () => {
    if (window.location.pathname === "/homepage") {
        const { initHomepageScenes } = await import("./scenes/homepage.js");
        initHomepageScenes(controller);
    }

    if (window.location.pathname === "/about") {
        const { initAboutScenes } = await import("./scenes/about.js");
        initAboutScenes(controller);
    }
});

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


Динамическая загрузка анимаций с GSAP

Многие эффекты ScrollMagic зависят от GSAP. Чтобы не тянуть всю библиотеку сразу, её также можно загружать динамически:

async function loadAnimation(sceneSelector) {
    const { gsap } = await import("gsap");
    const { ScrollMagicPluginGsap } = await import("scrollmagic-plugin-gsap");

    ScrollMagicPluginGsap(ScrollMagic, gsap);

    const scene = new ScrollMagic.Scene({
        triggerElement: sceneSelector,
        duration: 500
    })
    .setTween(gsap.to(sceneSelector, { opacity: 1, y: 50 }))
    .addTo(controller);
}

Загрузка GSAP и плагина происходит только при необходимости, что ускоряет первичный рендер страницы.


Lazy Loading сцен по скроллу

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

const sections = document.querySelectorAll(".scroll-section");

sections.forEach(section => {
    const observer = new IntersectionObserver(async (entries) => {
        if (entries[0].isIntersecting) {
            const { createScene } = await import(`./scenes/${section.dataset.scene}.js`);
            createScene(controller, section);
            observer.unobserve(section);
        }
    }, { threshold: 0.5 });

    observer.observe(section);
});

Каждая сцена загружается только тогда, когда она становится видимой хотя бы на 50% блока.


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

Для сборщиков вроде Webpack можно настроить dynamic import, который создаёт отдельные чанки автоматически:

// webpack.config.js
module.exports = {
    output: {
        filename: "[name].bundle.js",
        chunkFilename: "[name].chunk.js"
    },
    optimization: {
        splitChunks: {
            chunks: "all",
            maxInitialRequests: 5,
            minSize: 20000
        }
    }
};

Такой подход позволяет автоматически делить основной код и код сцен, включая плагины GSAP, ScrollMagic и пользовательские модули.


Best Practices для ScrollMagic с Code Splitting

  • Минимизировать основной бандл: оставлять только ScrollMagic Controller и базовые утилиты.
  • Динамически импортировать сцены: каждая сцена должна быть отдельным модулем.
  • Lazy load сложные анимации: особенно когда используется GSAP Timeline.
  • Использовать IntersectionObserver для отложенной инициализации сцен.
  • Следить за памятью: удалять сцены с помощью scene.destroy() после их завершения или выхода за пределы видимости, чтобы не накапливать лишние обработчики.

Пример модульной сцены

// scenes/homepage.js
import ScrollMagic from "scrollmagic";

export function initHomepageScenes(controller) {
    const scene1 = new ScrollMagic.Scene({
        triggerElement: "#hero",
        duration: 400
    })
    .setClassToggle("#hero", "fade-in")
    .addTo(controller);

    const scene2 = new ScrollMagic.Scene({
        triggerElement: "#features",
        duration: 600
    })
    .setClassToggle("#features", "slide-up")
    .addTo(controller);
}

Каждая сцена оформлена как отдельная функция, что упрощает динамический импорт и поддерживает разделение кода.


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