Jump to content
Search Community

ScrollSmoother, SplitText, NextJS

Olav test
Moderator Tag

Go to solution Solved by GreenSock,

Recommended Posts

Hi everyone,

 

I'm working on a project in NextJS using a few different gsap plugins. The main thing I'm not getting to work in this component is the SplitText animation (which I based on this codepen). My code looks like this. As you can see, I've commented out the part that makes the split text animation fire. When I try to run with the code enabled, I'm getting the following error. Can anyone explain to me what is going wrong? I'm really trying to understand and learn gsap in combination with nextjs, so if you see any other improvements I can make to the component they will be gladly appreciated. Also, I was wondering how I could do something such as:

new SplitText( el.current "p", { ...

When I only want to select the <p> tag's within my component. I really appreciate your time reading and maybe answering in advance, have been hitting my head and googling all evening but can't get it too work properly.

 

Kind regards,

Olav

See the Pen pxBMvy by PointC (@PointC) on CodePen

Link to comment
Share on other sites

I'm not a Next.js guy at all, sorry. It's pretty tough to troubleshoot without a minimal demo - can you provide one? 

 

Also, if you want to just get all the "p" elements that are descendants of el.current, you can do this: 

el.current.querySelectorAll("p")

 

Please don't include your whole project. Just some colored <div> elements and the GSAP code is best (avoid frameworks if possible). See if you can recreate the issue with as few dependancies as possible. If not, incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, then at least we have a reduced test case which greatly increases your chances of getting a relevant answer.

 

Here's a Next Starter Template that you can fork.

 

Can you see if REMOVING the stagger resolves things? I think there was a bug related to that in a recent version (it's already fixed in the upcoming released) - it would only happen if a staggered tween was created inside a gsap.context() and then it was reverted. 

 

Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

Link to comment
Share on other sites

I've managed to resolve the original error of this post, yet I am still wondering if my code is "correct" for react/nextjs. 

My tsx code now looks like this, which doesn't error, but also doesn't play properly.

 

export default function Footer({ smoother }: { smoother: any }) {
  const showScrollMd = useRef<HTMLButtonElement>({} as HTMLButtonElement)
  const handleClick = () => {
    smoother.current.scrollTo(0, true)
  }

  useEffect(() => {
    if (!showScrollMd.current) return
    showScrollMd.current.classList.add('xl:flex')
  }, [])

  const el = useRef<HTMLDivElement | null>(null)

  useIsomorphicLayoutEffect(() => {
    const ctx = gsap.context(() => {
      gsap.from('#crftd path', {
        yPercent: 100,
        stagger: 0.06,
        scrollTrigger: {
          trigger: el.current,
          start: 'top bottom',
          end: 'top 30%',
          scrub: true,
        },
      })

      const targets = gsap.utils.toArray('.reveal')
      targets.forEach((target: Element) => {
        const childSplit = new SplitText(target, {
          type: 'lines',
          linesClass: 'lineChild',
        })
        const parentSplit = new SplitText(target, {
          type: 'lines',
          linesClass: 'lineParent',
        })
        gsap.from(childSplit.lines, {
          yPercent: 100,
          stagger: 0.18,
          duration: 1,
          ease: 'power4.out',
          scrollTrigger: {
            trigger: target,
            markers: true,
            start: 'top 75%',
            end: 'bottom center',
          },
        })
      })
    }, el)
    return () => ctx.revert()
  }, [])
  return (
   <footer
      id="crftd-footer"
      ref={el}
      className="bg-crftd-black pb-6 pt-16 text-crftd-white md:pb-12 md:pt-24 xl:pt-36"
    >
        // a lot of html, you can see it here: https://github.com/olavgit/crftd/blob/main/components/footer/footer.tsx
        </footer>

 

I've recreated the SplitText/ScrollTrigger effect in a

See the Pen ExdwPeb by olavgit (@olavgit) on CodePen

, where it works flawlessly. I've included a very low quality screen recording of how it acts in my localhost, it is also visible on the live site. Can anyone explain to me why the text reveal animation isn't playing properly, the text just appears out of nowhere? And also, the logo.svg has a staggered effect which works fine, but has some sort of flash of unstyled content which I can't seem to get rid off. When you scroll down the first time, the letters suddenly jump to their animated positions. Thanks in advance for your time reading!

Link to comment
Share on other sites

Hi,

 

Unfortunately we can't do much with a live site.

 

Please create a minimal demo with the NextJS starter template that shows the problem you're having with the footer:

https://stackblitz.com/@GreenSockLearning/collections/gsap-nextjs-starters

 

If you need to use a Bonus plugin install the GSAP Trial package or use the template with ScrollSmoother in it.

 

Also you might want to have a look at this:

 

Happy Tweening!

Link to comment
Share on other sites

Hi @Rodrigo

 

I've made a demo in Stackblitz according to your instructions, which show both of my problems.

 

Check it out here: https://stackblitz.com/edit/nextjs-wuuj6h?file=app/page.js

 

For the first issue, the orange and light blue boxes 'c' and 'd' don't animate properly, they just jump into place when they hit the scroll trigger. Without a scroll trigger, the animations work fine. What am I doing wrong?

 

For the second issue, the logo at the bottom of the page is still in 'native' position when you first scroll down. Then while scrolling down, you can see the individual paths jump to their animated, 'correct' position. I've already had a look at the FOUC article you shared, I'm also following the CreativeCodingClub course by @Carl to learn all about it, but can't seem to get it to work properly.

 

Also, next to my 2 problems, am I not making any other mistake's for react/nextjs in my gsap code. I'm trying to learn the syntax and hooks and all, this works, but don't know if its how its supposed to be.

 

I hope this demo is enough to look at my problems, I really appreciate your time in advance,

 

Kind regards.

Link to comment
Share on other sites

18 hours ago, Olav said:

For the first issue, the orange and light blue boxes 'c' and 'd' don't animate properly, they just jump into place when they hit the scroll trigger. Without a scroll trigger, the animations work fine. What am I doing wrong?

I've done some further testing and fount out that this first part has got to do with the ScrollSmoother I'm using. I disabled scroll smoothing on touch devices, because a pinned scroll trigger was laggy. Since I disabled the scroll smoother on touch devices, the animations fire up perfectly fine (only on the touch devices tho). And idea's for the desktop version, with scrollsmoother enabled? And for the second part, the FOUC, I've also done more experimenting but can't come up with anything that works.

Link to comment
Share on other sites

Another update, the second problem;

On 5/1/2023 at 6:54 PM, Olav said:

For the second issue, the logo at the bottom of the page is still in 'native' position when you first scroll down. Then while scrolling down, you can see the individual paths jump to their animated, 'correct' position. I've already had a look at the FOUC article you shared

Is now fixed, I added a gsap.set for yPercent: 100, and changed the from tween to a to tween. Now there is no more FOUC.

 

Yet I'm still really stuck on my first problem, also visible in the minimal demo I provided. Also, when I enable scrub, everything seems to work fine, but when I just want the animation to play when it hits the scroll trigger, it doesn't work as expected. I'm comparing to example's online and am probably really close to the answer, if anyone can see what's going wrong, it's really appreciated

Link to comment
Share on other sites

Hi,

 

Sorry about the issues, the main problem here is order of operation. The ScrollSmoother instance should be created first and then the ScrollTrigger ones, as seen here:

See the Pen wvYrLVE by GreenSock (@GreenSock) on CodePen

 

If you comment out lines 3 through 6 and uncomment lines 21~26 you'll get the same result you have right now. I was hoping that the refresh method could solve this, but apparently it doesn't right now.

 

The problem is that React renders child components first and the works it's way up the component tree, so in your case the page gets rendered first and then the layout component.

 

We're looking into this right now, so please stand by. What you can do in the mean time is create a React Context that tracks the page rendering in the layout and informs the pages components that the ScrollSmoother instance is ready so the ScrollTrigger instances can be created now.

 

https://react.dev/learn/passing-data-deeply-with-context

https://react.dev/reference/react/createContext

 

Hopefully this helps for now.

Happy Tweening!

Link to comment
Share on other sites

Hi @Rodrigo,

 

Thanks for your reply, I'd be happy to implement this into my project. Do you maybe have an example on how to implement the context in the layout page, and how to make scroll triggers in other components wait for that context? I'll be implementing this effect in a lot of other components aswell. I've had a look at the docs and understand what's supposed to be happening, but have no clue on how to implement it correctly. I'm really looking forward to your reply!

Link to comment
Share on other sites

Sorry about any confusion there - I made an improvement to the next release that should work around this. In the meantime, you could put this code AFTER you do your ScrollSmoother.create():

ScrollTrigger.getAll().forEach(t => t.animation && !t.animation.vars.immediateRender && t.animation.render(0, true, true));

 

  • Like 1
Link to comment
Share on other sites

@GreenSock, thanks! I had the context solution working, but this seems to work aswell. Only thing is, my animations are in a footer component (applied in the standard layout), when I go to another page, the exact same thing happens again, where the animations don't fire properly and just appear without animating.

Link to comment
Share on other sites

3 minutes ago, Olav said:

@GreenSock, thanks! I had the context solution working, but this seems to work aswell. Only thing is, my animations are in a footer component (applied in the standard layout), when I go to another page, the exact same thing happens again, where the animations don't fire properly and just appear without animating.

Hm, do you have a minimal demo that clearly illustrates the issue? That'd be super helpful.

Link to comment
Share on other sites

2 hours ago, GreenSock said:

Hm, do you have a minimal demo that clearly illustrates the issue? That'd be super helpful.

I've spent some time creating a demo which shows my problems perfectly, I hope it'll be useful in finding a solution.

 

See it here: https://stackblitz.com/edit/nextjs-jbcb2g?file=app/layout.js

 

I've recreated the original reason for creating this post, I have some effects which should reveal text from below (box-c and box-d in the demo), but the animation doesn't fire properly. Instead of playing the animation, the elements jump into place once you hit the scrolltrigger. If you scroll fast enough, you can still catch part of the animation playing, so I guess the animation is playing directly on load, not waiting for the scroll trigger.

 

The second effect is place in the <Footer /> component, which is loaded in the layout.js file. The animation also uses a scroll trigger. For starters, it doesn't set the .from position properly, since you can still see the path's jump into place when scrolling down the page. Once they are in the correct position, the animation seems to work fine, until you navigate to another page. Try going the 'ipsum' page in the menu, the footer animation freezes and also the scroll trigger markers disappear.

 

I've also implemented your posted solution in the layout file @GreenSock, but it doesn't seem to work right now. Other than that this is the starter file made by greensocklearning, so no other configuration done by me.

Link to comment
Share on other sites

  • Solution

Alright, I think I got it pretty well figured out and the next release of GSAP will smooth this out. In the meantime, here's a forked Stackblitz with some workarounds implemented in the file where you've got your ScrollSmoother created: 

https://stackblitz.com/edit/nextjs-3okv28?file=app%2Flayout.js

 

It's related to how Next.js is creating/killing/creating stuff and the order in which it's happening (ScrollSmoother last). Thanks for your patience. 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Although it works perfectly in javascript, I'm getting some errors in my typescript project. I won't bother to fix the ones that won't be necessary after version 3.11.5, since I can throw those out anyway, after the update.

 

        existingTriggers.forEach((t) => t.revert(true, true)); // revert existing ScrollTriggers

On this line, I'm getting a TS2339: Property 'revert' does not exist on type 'ScrollTrigger'. I'm not a typescript hero at all, but this is a line that I'm supposed to keep. How would I fix that?

 

Also for my useRef's I've been using a type of <any>, what would the correct types for these be?

 

const smoother = useRef<any>(null);
const ctx = useRef<any>(null);

 

Link to comment
Share on other sites

Yeah, I'm not much of a TypeScript guy. Sorry. You should be able to ignore those warnings. There's a revert() method, but currently I'd consider that private (not really for public use - that's why we haven't documented it or put it in the TypeScript definitions). 

 

As for the smoother and ctx types, I know that the GSAP TypeScript definition files have a type for ScrollSmoother as well as for Context (that one is probably gsap.Context). 

 

If you'd like me to send you a .tgz file of the beta release, I'd be happy to do that. 

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...