SSR и ScrollMagic

ScrollMagic — это библиотека JavaScript, позволяющая создавать анимации и эффекты на основе прокрутки страницы. Она изначально ориентирована на работу в браузере, что создаёт определённые трудности при использовании в средах с серверным рендерингом (SSR, Server-Side Rendering), таких как Next.js или Nuxt.js. Основная проблема заключается в том, что ScrollMagic оперирует объектами браузера (window, document), которые на сервере недоступны.

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

if (typeof window !== 'undefined') {
    const ScrollMagic = require('scrollmagic');
    const controller = new ScrollMagic.Controller();
}

Это гарантирует, что сервер не будет пытаться выполнять код, зависящий от DOM.


Инициализация контроллера ScrollMagic

Контроллер (ScrollMagic.Controller) — это центральный объект, управляющий всеми сценами (ScrollMagic.Scene). При использовании SSR важно создавать контроллер только на клиенте, после того как DOM полностью доступен:

useEffect(() => {
    const controller = new ScrollMagic.Controller();

    return () => controller.destroy(true);
}, []);

Здесь используется хук useEffect для React или аналогичные события mounted в Vue, чтобы гарантировать выполнение кода на клиенте.


Создание сцен в условиях SSR

Сцена (ScrollMagic.Scene) связывает конкретный элемент с анимацией. Для SSR подход остаётся прежним: создание сцены должно происходить только после того, как элемент доступен в DOM.

useEffect(() => {
    if (typeof window === 'undefined') return;

    const controller = new ScrollMagic.Controller();
    const scene = new ScrollMagic.Scene({
        triggerElement: '#animateMe',
        duration: 300,
        offset: 50
    })
    .setClassToggle('#animateMe', 'visible')
    .addTo(controller);

    return () => {
        scene.destroy(true);
        controller.destroy(true);
    };
}, []);

Ключевые моменты:

  • triggerElement должен существовать на момент создания сцены.
  • duration задаёт протяжённость анимации по скроллу.
  • offset позволяет сдвинуть начало триггера относительно элемента.

Интеграция с анимациями и GSAP

ScrollMagic часто используется вместе с библиотекой анимаций GSAP (gsap). В SSR-контексте важно убедиться, что анимации создаются после рендера DOM:

useEffect(() => {
    if (typeof window === 'undefined') return;

    const controller = new ScrollMagic.Controller();

    const tween = gsap.to("#animateMe", { duration: 1, x: 300, opacity: 1 });

    const scene = new ScrollMagic.Scene({
        triggerElement: "#animateMe",
        duration: 500
    })
    .setTween(tween)
    .addTo(controller);

    return () => {
        scene.destroy(true);
        controller.destroy(true);
    };
}, []);

Это позволяет плавно интегрировать ScrollMagic с любыми анимациями без ошибок на сервере.


Предотвращение ошибок при SSR

Наиболее частые ошибки при SSR связаны с попыткой доступа к window или document на сервере. Чтобы их избежать:

  1. Проверка окружения:

    const isClient = typeof window !== 'undefined';
  2. Динамический импорт ScrollMagic: В Next.js можно использовать:

    import dynamic from 'next/dynamic';
    const ScrollMagic = dynamic(() => import('scrollmagic'), { ssr: false });
  3. Инициализация внутри useEffect или mounted: гарантирует доступ к DOM и предотвращает ошибки рендера.


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

При использовании ScrollMagic на больших страницах важно учитывать нагрузку на скролл-обработчики:

  • Создавать минимальное количество сцен.
  • Использовать tweenChanges: true для плавных анимаций без постоянного пересчёта.
  • Уничтожать сцены и контроллеры при уходе со страницы.

Пример очистки ресурсов:

useEffect(() => {
    if (!isClient) return;

    const controller = new ScrollMagic.Controller();
    const scene = new ScrollMagic.Scene({ triggerElement: '#animateMe' })
        .setClassToggle('#animateMe', 'visible')
        .addTo(controller);

    return () => {
        scene.destroy(true);
        controller.destroy(true);
    };
}, []);

Использование ScrollMagic в фреймворках с SSR

Next.js: динамический импорт и использование useEffect предотвращают ошибки рендера. Nuxt.js: сцены создаются в mounted, контроллер уничтожается в beforeDestroy. Gatsby: проверка typeof window !== 'undefined' перед использованием ScrollMagic предотвращает ошибки на этапе сборки.