16

I would like to implement an animation to fade sections, like in this example, into my application. Therefore I've had a look at fullPage.js.

However, since I need to integrate it into a Next.js React app with server-side rendering I can't use it since it relays on jQuery, which doesn't support SSR. Therefore I've tried my luck with ScrollMagic, which doesn't relay on jQuery. But it also doesn't support SSR (needs window), therefore I've initialized it in the componentDidMount() method and even loaded it there (like it's recommended here).

It currently works initially, but as soon as you change the page and an AJAX request is done and Next.js replaces the page, an error will be thrown (see below):

Node was not found

Node was not found React error when using ScrollMagic with Next.js

I've tried to destroy ScrollMagic before the AJAX request in componentWillUnmount(), but with no luck. I can't figure out what's wrong and unfortunately, I couldn't find any documentation about ScrollMagic with React or Next.js.

This is my entire component:

import React from 'react';
import PropTypes from 'prop-types';

class VerticalSlider extends React.Component {
  constructor(props) {
    super(props);
    this.ScrollMagic = null;
    this.controller = null;
    this.scenes = [];
    this.container = React.createRef();
  }

  componentDidMount() {
    if (this.container.current) {
      // Why "require" here?
      // https://github.com/zeit/next.js/issues/219#issuecomment-393939863
      // We can't render the component server-side, but we will still render
      // the HTML
      // eslint-disable-next-line global-require
      this.ScrollMagic = require('scrollmagic');
      this.initScroller();
    }
  }

  componentWillUnmount() {
    this.scenes.forEach(scene => {
      scene.destroy();
    });
    this.controller.destroy();
    this.scenes = [];
    this.controller = null;
  }

  initScroller() {
    try {
      this.controller = new this.ScrollMagic.Controller();
      if (this.container.current !== null && this.container.current.children) {
        [...this.container.current.children].forEach(children => {
          const scene = new this.ScrollMagic.Scene({
            triggerElement: children,
            duration: window.innerHeight * 1.5,
            triggerHook: 0,
            reverse: true
          });
          scene.setPin(children);
          this.scenes.push(scene);
        });
        this.controller.addScene(this.scenes);
      }
    } catch (e) {
      console.log(e);
    }
  }

  render() {
    return (
      <div ref={this.container}>
        {this.props.sections}
      </div>
    );
  }
}

VerticalSlider.propTypes = {
  sections: PropTypes.arrayOf(PropTypes.node).isRequired
};

export default VerticalSlider;
dude
  • 5,678
  • 11
  • 54
  • 81
  • Which version of Next.js are you using? – bennygenel Jun 09 '18 at 07:50
  • Next.js version ^6.0.3 – dude Jun 10 '18 at 13:19
  • can you reproduce it here https://repl.it/site/languages/nextjs? – amankkg Jun 13 '18 at 17:59
  • seems to be working https://repl.it/@code4aman/RegalFarawayBlogclient – amankkg Jun 13 '18 at 18:17
  • @amankkg Yes, it works initially. But, like mentioned in my post, as soon as you have multiple pages it no longer works. Also, the VerticalSlider component shouldn't be a page component. – dude Jun 14 '18 at 06:52
  • It would be great if you can provide us a broken example. I updated my repl.it example to demo a navigation also. Anyway, there is nothing special with nextjs and scrollmagic using together and VerticalSlider seems to be working. However, I slightly refactored it. – amankkg Jun 14 '18 at 19:20
  • I found this module on npm https://www.npmjs.com/package/scrollmagic-with-ssr maybe it helps – Frank Jan 12 '19 at 11:38
  • 1
    This is probably not a good answer for SO but I thought maybe this would help. React has a really good and well maintained transition library called [`react-transition-group`](https://github.com/reactjs/react-transition-group) and Next.js has a similar library called [`next-page-transitions`](https://github.com/illinois/next-page-transitions/blob/master/README.md). There is a good [example](https://github.com/zeit/next.js/tree/canary/examples/with-next-page-transitions) showing how to use it. Example uses `_app.js` for easily animating every page transition. I suggest you to take a look at tha – bennygenel Jun 10 '18 at 13:55
  • Thanks but unfortunately that's not what I need. – dude Jun 11 '18 at 06:30

3 Answers3

3

Would fullpage.js not be more suited for you needs?

You should be able to map your routes and then build each slide as a placeholder for the page using the app.js

Otherwise, I should have a working example of scroll magic, I will look for the repo and share it once found.

UPDATE: Here is an example of next.js using fullpage.js

However, since I need to integrate it into a Next.js React app with server-side rendering I can't use it since it relays on jQuery

fullpage.js no longer depends on jquery and it supports SSR as well.

Paul van Dyk
  • 868
  • 10
  • 8
1

Although in some other venues people suggested using next/dynamic and importing the ScrollMagic library with the option 'ssr:false', I did not have success with this tactic. The ScrollMagic object was not returned correctly.

What ended up working for me was to use a modified version of the ScrollMagic library (modified by Jan Fischer: https://github.com/bitworking) in which the whole ScrollMagic library is wrapped "in a self invoking function so that I can mock the window and document object." I copied this modified ScrollMagic to my next.js project and referenced it instead of including the package as a node module.

Here is the modified file: https://raw.githubusercontent.com/bitworking/react-scrollmagic/master/src/lib/scrollmagic.js

Erin Geyer
  • 11,754
  • 1
  • 24
  • 24
1

You can get scroll magic to work with next.js by doing a dynamic import.

useEffect(() => {
    const load = async () => {
      if (typeof window !== undefined) {
        const ScrollMagic = (await import('scrollmagic')).default;
        const controller = new ScrollMagic.Controller();

        new ScrollMagic.Scene({
          triggerElement: '#one',
          triggerHook: 0.5,
          reverse: true,
        })
          .on('enter', function (e: any) {
            console.log('enter');
          })
          .on('leave', function (e: any) {
            console.log('leave');
          })
          .addTo(controller);
      }
    };
    load();
  }, []);
AC88
  • 125
  • 2
  • 4
  • 17