Jump to content
Search Community

useGSAP, FOUC, Sequence difficulties.

imksmv test
Moderator Tag

Go to solution Solved by Cassie,

Recommended Posts

Can someone help, I'm trying to avoid the flesh of the text on the screen (FOUC). Also I'm trying to animate the text one by one, but seems like the timeline does not work as it supposed to.

 

 

'use client'

 

 import gsap from 'gsap-trial'

 import { useGSAP } from '@gsap/react'

 import { useRef } from 'react'

 import SplitText from 'gsap-trial/SplitText'

 

 gsap.registerPlugin(SplitText)

 

 export default function Page() {
   const container = useRef<HTMLElement | null>(null)
   const tl = useRef<GSAPTimeline | null>(null)

 

    useGSAP(
     () => {
       const titles = gsap.utils.toArray<Element>('p', container.current)

 

       titles.forEach(title => {
         const splitTitle = new SplitText(title)

 

         tl.current = gsap
           .timeline()
           .from(
             splitTitle.lines,
             { opacity: 0, y: 20, stagger: 0.02, autoAlpha: 0 },
             '<'
           )
           .to(
             splitTitle.lines,
             { opacity: 0, y: -20, stagger: 0.02, autoAlpha: 1 },
             '<1'
           )
       })
     },
     { scope: container }
   )

 

   return (
     <section
       ref={container}
       className='flex justify-center items-center h-screen'
     >
       <div className='text-2xl leading-[0px] invisible'>
         <p>My name is Illia</p>
         <p>I am a cool guy</p>
         <p>Great developer</p>
         <p>and a funny fellow</p>
       </div>
     </section>
   )
 }

Link to comment
Share on other sites

Without a minimal demo, it's very difficult to troubleshoot; the issue could be caused by CSS, markup, a third party library, a 3rd party script, etc. Would you please provide a very simple CodePen or Stackblitz that illustrates the issue? 

 

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

 

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

that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

 

Using a framework/library like React, Vue, Next, etc.? 

CodePen isn't always ideal for these tools, so here are some Stackblitz starter templates that you can fork and import the gsap-trial NPM package for using any of the bonus plugins: 

 

Please share the StackBlitz link directly to the file in question (where you've put the GSAP code) so we don't need to hunt through all the files. 

 

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

Here is the link to Stackblitz https://stackblitz.com/edit/stackblitz-starters-pvzygv?file=app%2Fglobals.css,tailwind.config.ts,README.md,app%2Flayout.tsx,app%2Fpage.tsx

I'm using tailwind alongside, I've tried using "invisible" class on those "p"  elements and autoAlpha, but with no success, the FOUS still remains. Also there is no sequence between those elements on appearance. They should appear one by one, but instead they appear all together immediately.

Link to comment
Share on other sites

I'm not a Next.js guy, but it looks to me like this is purely a Next.js behavior issue where it renders the page first, then when hydration is done it goes back and re-renders at which time it fires useEffect/useLayoutEffect/useGSAP hooks. Even if you completely remove GSAP from the equation, and you directly set the elements' style (maybe opacity), for example, you'll see that same behavior (FOUC). 

 

One solution I've seen from a quick search online is to set the initial opacity of the container to be 0 so that the user doesn't see it initially, and then in your hook you can set opacity to 1 immediately: 

https://stackblitz.com/edit/stackblitz-starters-7qfdp2?file=app%2Fpage.tsx

 

Also, I'd recommend running gsap.registerPlugin(useGSAP) too. That's only helpful when you're running the gsap-trial in an online environment like Stackblitz. It will never hurt to register useGSAP...and it's pretty much never required in a "real" project that you compile locally, but it is helpful in these online environments. 

 

To summarize - the FOUC has nothing to do with GSAP - it's a Next.js thing. 🙂

  • Like 1
Link to comment
Share on other sites

Okay, I see, thanks! Also I'm trying to animate the text one by one, but seems like the timeline does not work as it supposed to. So I put all the text in one place, and expected it to animate one by one on the middle. So - the first "p" element appears and then disappears, after that the second "p" element appears and then disappears and so on... But for some reason the timeline doesn't work in my case.

Link to comment
Share on other sites

  • Solution

Hey there!

 

So the timeline is working, but you're trying to stagger multiple elements *inside* a for loop (where you only have access to one element at a time)

Rather than stagger, you could do something like this where you define the timeline outside of the loop, then loop round and add each line to the timeline

const timeline = gsap.timeline();
const titles = gsap.utils.toArray<Element>('p', container.current);

// loop round the titles
titles.forEach((title) => {
  const splitTitle = new SplitText(title);

  // add each title to the timeline
  timeline
   .from(splitTitle.lines, { opacity: 0, y: 20 })
   .to(splitTitle.lines, { opacity: 0, y: -20 });
});

https://stackblitz.com/edit/stackblitz-starters-mcygak?file=app%2Fpage.tsx

Alternatively you could push all the lines into an array and then stagger them - something like this

const titles = gsap.utils.toArray<Element>('p', container.current);
const lines = []
// loop round the titles
titles.forEach((title) => {
  const splitTitle = new SplitText(title);
  lines.push(splitTitle.lines)
});

// add each title to the timeline
 gsap.timeline()
  .from(lines, { opacity: 0, y: 20, stagger: 0.2 })
  .to(lines, { opacity: 0, y: -20, stagger: 0.2 });

(sorry I don't understand typescript to adjust that for you)

Hope this helps!

  • Like 3
Link to comment
Share on other sites

Thank you for your help, Cassie!

I have another question:  So, in this code: https://stackblitz.com/edit/stackblitz-starters-s1l9cd?file=app%2Fpage.tsx, If I decide to use context to check the length of data, it shows 0, however there is a teen that gets executed on render after which I call console.log(context.data.length) which shows 0, but  expected to show 1. I have tried implemented on button click using contextSafe, but it's the same result.
 

Link to comment
Share on other sites

I’m not at my desk right now, but from a quick glance, it looks like the problem is that you didn’t register useGSAP as a plugin: 

gsap.registerPlugin(useGSAP, ScrollTrigger);

Normally that isn’t really necessary but since you’re using the gsap-trial package too and it’s in a special environment like Stackblitz, you’ve technically got 2 different gsap objects being loaded. The one that’s creating the context in the useGSAP() hook is different than the one you’re using to create your actual animations. You don’t really need to understand all that - the solution is probably to just register the useGSAP hook as a plugin. Does that resolve things for you? 

  • Like 1
Link to comment
Share on other sites

Hi,

 

That's because the GSAP core instance is the same in both the useGSAP hook and the GSAP instance you installed as Jack already explained here:

On 2/14/2024 at 3:18 PM, GreenSock said:

you’ve technically got 2 different gsap objects being loaded. The one that’s creating the context in the useGSAP() hook is different than the one you’re using to create your actual animations.

In the case of your last demo, the GSAP object is the same. Registering the useGSAP hook as a plugin is only needed in this very particular case, when you have the gsap-trial package. The gsap object from the gsap-trial package is different from the gsap object in the useGSAP hook.

 

In your code you'd have this with gsap-trial:

import gsap from "gsap-trial"; 

While internally the hook does this:

import gsap from "gsap";

That means that in the same project there are two different gsap packages installed (all are the latest version though), gsap and gsap-trial, the first is used by the hook and the other is there in order to use the bonus plugins in these type of development environments, that's all.

 

Hopefully this clear things up.

Happy Tweening!

  • Like 2
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...