Jump to content
Search Community

NextJS ScrollTrigger Issue/Question

Chey Flammer test
Moderator Tag

Recommended Posts

Sorry I am not sure how to upload React content to CodePen yet. Hopefully I can explain well enough. 

I am trying to achieve a ScrollTrigger affect with the ".hr" lines in my app. I have them within my Hero component and also within my Header component. Both those components I am rendering out in App so I figured that would be the best place to include my GSAP code for that animation AND it works for a regular animation ".from()". So my first deal of business is why is my ScrollTrigger not working? (the commented out code). My second question is; the Hero component is the first thing to render and so I would ultimately like those ".hr"s in the Hero to render on page load and the Header ones to load once I start scrolling to them. So would it be better to have a ".hero-hr" class that uses the working animation and then a ".hr" for my scroll trigger ones in Header? Thanks guys! :)

 

App.js

```

export default function App() {
  const appRef = useRef();
  useLayoutEffect(() => {
    let lines = gsap.utils.toArray('.hr');
    let ctx = gsap.context(() => {
      let tl = gsap.timeline();
      // NOT WORKING
      // lines.forEach((line) => {
      //   tl.from(line, {
      //     width: 0,
      //     duration: 3,
      //     ease: 'power4',
      //     scrub: true,
      //     scrollTrigger: {
      //       trigger: line,
      //       start: 'top 80%',
      //       toggleActions: 'play none none reverse',
      //     },
      //   });
      // });
      // WORKING
      gsap.from('.hr', {
        width: 0,
        ease: 'power4.out',
        delay: 0,
        stagger: {
          amount: 0.3,
        },
        autoAlpha: 0,
        duration: 1.8,
      });
    }, appRef);
    return () => ctx.revert();
  }, []);
  return (
    <div className='wrapper' ref={appRef}>
      <Hero />
      <Header order='001' title='about' />
      <About />
      <Header order='002' title='projects' />
      <Projects />
    </div>
  );
}

```

Hero.js

```

const Hero = () => {
  const heroRef = useRef();
  useLayoutEffect(() => {
    let ctx = gsap.context(() => {
      gsap.from('.reveal h1, .reveal h2', {
        y: 200,
        ease: 'power4.out',
        autoAlpha: 0,
        delay: 0,
        duration: 1.8,
      });
      gsap.from('p.text', {
        opacity: 0,
        y: -100,
        ease: 'power4.out',
        delay: 1,
        stagger: {
          amount: 0.3,
        },
        duration: 1.8,
        autoAlpha: 0,
      });
      gsap.from('.nav-item, .nav-item-two', {
        opacity: 0,
        y: 100,
        ease: 'power4.out',
        stagger: {
          amount: 0.3,
        },
        autoAlpha: 0,
        duration: 1.8,
      });
    }, heroRef);
    return () => ctx.revert();
  }, []);
  return (
    <section ref={heroRef}>
      <div className='row'>
        <div className='reveal'>
          <h1 className='primary'>Redacted</h1>
        </div>
        <div className='reveal'>
          <h1 className='primary'>Redacted</h1>
        </div>
      </div>
      <div className='hr'></div>
      <div className='row'>
        <p className='text'>web design</p>
        <p className='text'>
          <Link href='' className='hero-email'>
            email
          </Link>
        </p>
        <p className='text'>brand development</p>
        <p className='text'>Redacted</p>
      </div>
      <div className='white-space'></div>
      <div className='row'>
        <div className='nav-item-two'>
          <p className='item'>
            <Link href='' className='link'>
              ABOUT
            </Link>
          </p>
          <p className='id'>001</p>
        </div>
        <div className='reveal'>
          <h2 className='secondary'>Redacted</h2>
        </div>
        <div className='nav-item'>
          <p className='item'>
            <Link href='' className='link'>
              PROJECTS
            </Link>
          </p>
          <p className='id'>002</p>
        </div>
      </div>
      <div className='hr'></div>
      <div className='row'>
        <div className='reveal'>
          <h2 className='secondary'>Web</h2>
        </div>
        <div className='nav-item'>
          <p className='item'>
            <Link href='' className='link'>
              Socials
            </Link>
          </p>
          <p className='id'>003</p>
        </div>
        <div className='reveal'>
          <h2 className='secondary'>Developer</h2>
        </div>
      </div>
      <div className='hr'></div>
      <div className='row'>
        <div className='reveal'>
          <h2 className='secondary'>Based in</h2>
        </div>
        <div className='nav-item-two'>
          <p className='item'>
            <Link href='#contact' className='link'>
              contact
            </Link>
          </p>
          <p className='id'>004</p>
        </div>
        <div className='reveal'>
          <h2 className='secondary'>California</h2>
        </div>
      </div>
      <div className='hr'></div>
    </section>
  );
};

```

Header.js

```

const Header = ({ title, order }) => {
  gsap.registerPlugin(ScrollTrigger);
  const headerRef = useRef();
  useLayoutEffect(() => {
    let ctx = gsap.context(() => {
      gsap.from('h3', {
        x: -100,
        ease: 'power4.out',
        opacity: 0,
        stagger: {
          amount: 0.8,
        },
        autoAlpha: 0,
        duration: 1.8,
      });
    }, headerRef);
    return () => ctx.revert();
  });
  return (
    <div className='section-header' ref={headerRef}>
      <div className='section-header-container'>
        <h3 className='section-title'>{title}</h3>
        <h3 className='section-order'>/{order}</h3>
      </div>
      <div className='hr'></div>
    </div>
  );
};

```

Link to comment
Share on other sites

You don't need to use CodePen - here's a React Starter Template in Stackblitz.

 

It looks like you're making one of the common mistakes - you're nesting tweens with ScrollTriggers inside of a timeline. It's logically impossible to have the tween's playhead controlled by BOTH a parent timeline AND the scrollbar position (those could be going in completely different directions). You should either create standalone tweens that have scrollTriggers -OR- create one timeline that has a ScrollTrigger and nest your tweens in that (but don't have scrollTriggers on those tweens). 

 

I hope that clears things up. If you still need help, please make sure you just fork that starter template, edit it to illustrate the issue you're facing, and post it back here so we can see the problem in context. 

Link to comment
Share on other sites

I don't understand. My code looks practically identical to the StackBlitz you posted? Even if I only have one function, like just the scroll trigger "useLayoutEffect()" in my component nothing. So how can it be within some other tween if it's it own function. The Hero components parent component now has 0 GSAP or JS at all so it doesn't have a timeline.

```

 

I just want that .hr which is a horizontal line to grow from a width of 0 to 100% when you scroll down to it. 

 

const Header = ({ title, order }) => {
  gsap.registerPlugin(ScrollTrigger);
  const headerRef = useRef();
  useLayoutEffect(() => {
    let ctx = gsap.context(() => {
      let lines = gsap.utils.toArray('.hr');
      lines.forEach((line) => {
        gsap.from(line, {
          width: 0,
          scrollTrigger: {
            trigger: line,
            start: 'top 80%',
            toggleActions: 'play none none reverse',
          },
        });
      });
    }, headerRef);
    return () => ctx.revert();
  });
  return (
    <div className='section-header' ref={headerRef}>
      <div className='section-header-container'>
        <h3 className='section-title'>{title}</h3>
        <h3 className='section-order'>/{order}</h3>
      </div>
      <div className='hr'></div>
    </div>
  );
};
 
export default Header;

```

 

I also can't find a fork option on the StackBlitz 

 

Would posting my Github repo with a link to the live site on Vercel help? 

Link to comment
Share on other sites

There should be a big "fork" button on the upper left corner. 

 

Here's a quick fork where I tossed your stuff in: 

https://stackblitz.com/edit/react-zdqnro?file=src%2FApp.js

 

You forgot to pass in an empty dependency Array to the useLayoutEffect() which would make it get called on every render, by the way. 

 

I also wasn't sure if you wanted the behavior to be scrubbed or not. I just left your toggleActions in there, assuming that's what you wanted.  

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...