Jump to content
Search Community

React Complex Timeline Animation

TomS0 test
Moderator Tag

Recommended Posts

Hey everyone,

 

I've been doing some searching around the forum about doing complex timeline based animations with React. I have found some answers that I don't think are up to date as they don't currently work. For example:


Applying `const timeline = gsap.timeline()` with `useEffect()` will reset the timeline when the component re-renders as it redefines the `const`. I have decided to go for `const timeline = useMemo(() => gsap.timeline({ paused: true }), [])`. I'm unsure if this is best practice, but it isn't giving me any problems really.

The one that is giving me a problem however is

const imageRef = useRef(null)
  const [hovered, setHovered] = useState(false)
  const timeline = useMemo(() => gsap.timeline({ paused: true }), [])

  useEffect(() => {
    timeline
      .fromTo(
        imageRef.current,
        {
          clipPath: 'polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)',
          rotate: -10,
          y: 20,
          autoAlpha: 0,
        },
        {
          clipPath: 'polygon(0% 65%, 100% 35%, 100% 100%, 0% 100%)',
          rotate: 0,
          y: 5,
          autoAlpha: 0.4,
          duration: 0.15,
        }
      )
      .to(imageRef.current, {
        clipPath: 'polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)',
        rotate: 5,
        autoAlpha: 1,
        y: 0,
      })

    return () => {
      timeline.kill()
    }
  }, [timeline])

  function onEnter() {
    setHovered(true)
    timeline.play()
  }

  function onLeave() {
    setHovered(false)
    timeline.reverse()
  }

Because React in development mode invokes a double render, when you fire `onEnter` the timeline skips, this doesn't happen in the deployed site because of the lack of double render. But I'm unsure exactly what it's happening. 

Is `useMemo` the incorrect hook to be using for this use case? I tried doing `useRef` then assigning gsap to `useRef.current` but that completely stopped the page from rendering giving a timeout.

 

Thanks,

Tom

 

PS: 
Please release an official React module, I will love you forever!

Link to comment
Share on other sites

A few things:

  1. afaik there will never be an official react module, GSAP is framework agnostic.
  2. There are currently features in the work that will make working with GSAP in react easier
  3. The best way to deal with strict mode until (2) is fully released is to disable strict mode.
  4. Store timelines in refs. I do not know why your program is bombing when you do that, but it shouldn't.
  5. An effect with GSAP in it pretty much never needs to rerun and should have an empty dependency array unless you need an animation to run on some state change.
  • Like 3
Link to comment
Share on other sites

  • 9 months later...

Hi! Just looking for a solution for a timeline I was working on in React I found out I was having this same problem. While ctx.revert() works fine, ctx.kill() doesn't. I don't know why to be honest. 
 

  const headerRef = useRef<HTMLElement>(null)
  const headerCtRef = useRef<HTMLDivElement>(null)

  const timeline = useRef<GSAPTimeline>(
    gsap.timeline({
      defaults: {
        ease: 'back.out',
      },
    })
  )

  useLayoutEffect(() => {
    const tl = timeline.current
    const header = headerRef.current
    const headerCt = headerCtRef.current
    const ctx = gsap.context(() => {
      tl.from(
        header,
        {
          yPercent: -100,
          duration: 0.5,
        },
        0
      )
        .from(
          headerCt,
          {
            rotateX: 35,
            delay: 0.1,
            duration: 0.5,
          },
          0
        )
        .from(
          [headerCt, header],
          {
            autoAlpha: 0,
            duration: 0.2,
          },
          0
        )
      
      ScrollTrigger.create({
        start: 'top top',
        end: 99999,
        onUpdate: (self) => {
          self.direction === -1 ? tl.play() : tl.reverse()
        },
      })
    }, headerRef)
    return () => {
      ctx?.revert()
    }
  }, [])

If I would use ctx.kill() the timeline bounces in and back no matter what I do, I suspect because of react strict mode second call.

Link to comment
Share on other sites

17 minutes ago, Joyboy said:

While ctx.revert() works fine, ctx.kill() doesn't

Yes, that makes total sense. React double-calls your useLayoutEffect(), so let's walk through what happens but to make things easy let's assume you are only doing a .from(...{opacity: 0}) to fade things in: 

  1. Your timelines are set up, and the from() tweens render things immediately at opacity: 0. Remember, a from() tween uses whatever the CURRENT value is as the destination value. Okay, so we're all set up to animate opacity from 0 -> 1
  2. React calls the useLayoutEffect() AGAIN, but right before it does that, it invokes the cleanup function from the first one which is where you are calling ctx.kill() or ctx.revert(). If you kill(), that doesn't change the state of anything - it simply STOPS (and disposes of) any animations/ScrollTriggers that were created inside that context. So if you kill(), the opacity will remain at 0. The inline css opacity: 0 stays there. If you ctx.revert(), however, it will revert all those animations, so opacity would return to what it was before you ever created that initial animation, thus opacity would be 1 at this point. 
  3. Now React creates those timelines again, so think about what happens with the from(...{opacity: 0}) animation. If you only did the ctx.kill() in your previous cleanup, opacity is CURRENTLY 0! Thus you'd be animating from 0 to 0 (no change). That's why reverting is essential. The whole point of React's cleanup function is so that you can clean things up and return them to their initial state. gsap.context() makes that super easy.

Does that clear things up? 

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