Jump to content
GreenSock

Joenha

Cant get .reverse() to reverse the animation in React(NextJS)

Go to solution Solved by Rodrigo,

Recommended Posts

Hi there,

New here (also new with gsap/react); I have made a side bar that slides out from the right side,

I got the slide in animation to work but can't use .reverse() to reverse that animation.
It immediately resets back to xPercent: 100 , instead of animating.

Anyone here able to give me any pointers on how I should proceed?

Small code snippet:

const [menuOpen, setMenuOpen] = useState<boolean>(false);
    const [toggle, setToggle] = useState<boolean>(false);

    const handleToggle = () => {
        setToggle(!toggle);
        setMenuOpen(prev => !prev);

    };
    const tl = gsap.timeline({ paused: true });

    useEffect(() => {

        tl.fromTo('.hamburger__overlay',
            {
                xPercent: 100,
                duration: 1,

            }, {
            xPercent: 0,
            duration: 1,
        }
        ).reverse()
        tl.reversed(!toggle);
    });

(Dont mind the styling it's just a quick demo ;) )
CodeSandbox link: https://codesandbox.io/s/sidebar-y45kf1

Link to comment
Share on other sites

  • Solution

Hi @Joenha and welcome to the GreenSock forums!

 

There are a few small issues in your setup, but nothing that we can't solve, so let's get to it!

 

First, there is no need for the two state properties menuOpen and toggle, since switching one should have he same effect in your React app, so just use one.

 

Second, it's always a good idea to store GSAP instances that will be toggled back and forward in a ref, so they are kept through re-renders (more on that in a moment).

 

Third, you are not passing any dependency to your useEffect hook, even though your entire component does just one thing, it's always a good idea, since a parent component could re-render and everything inside the useEffect will be executed again.

 

Fourth, since version 3.11 GSAP has the Context method that helps specially with frameworks like react.

 

The main problem is that you create a timeline on the first render, the click the button and toggle the timeline's direction, so far so good. The issue is that when you click the button again the state is updated and the timeline is created again and this time it's like nothing happens, but why? You're using a fromTo instance in a timeline that is immediately reversed, so when that instance is created again and it reversed state is set to true, it goes back, but it's already at 0 seconds, so it just stays there. GSAP is doing exactly what is supposed to. Here is where storing a GSAP instance that will be used over and over again in a ref comes in handy. The instance is created just once and then toggled when the state is updated, that's it.

 

This code maybe works the way you expect:

import React, { FC, useState, useEffect, useRef } from "react";
import { gsap } from "gsap";

const HamburgerMenu: FC = () => {
  const [toggle, setToggle] = useState<boolean>(false);
  const tl = useRef<GSAPTimeline>(gsap.timeline({ paused: true }));
  const hamburgerOverlay = useRef<HTMLDivElement | null>(null);

  const handleToggle = () => {
    setToggle(!toggle);
  };

  useEffect(() => {
    gsap.set(hamburgerOverlay.current, { xPercent: 100, });
    const ctx = gsap.context(() => {
      tl.current.to(
        hamburgerOverlay.current,
        {
          xPercent: 0,
          duration: 1
        }
      ).reverse();
    });
    
    tl.current.reversed(!toggle);
    return () => ctx.revert();
  }, [toggle]);

  return (
    <div className="hamburger">
      {!toggle ? (
        <div onClick={handleToggle}>
          <div className="hamburger__toggle m-t-2 m-r-2">
            <div className="hamburger__icon"></div>
            <div className="hamburger__icon"></div>
          </div>
        </div>
      ) : (
        <div onClick={handleToggle}>
          <div className="hamburger__toggle m-t-2 m-r-2">
            <div className="hamburger__icon"></div>
            <div className="hamburger__icon hamburger__icon--active"></div>
          </div>
        </div>
      )}

      <div className="hamburger__overlay" ref={hamburgerOverlay}>
        <div className="hamburger__nav">
          <div className="hamburger__list">
            <div className="hamburger__btn btn btn--primary">contact me</div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default HamburgerMenu;

Happy Tweening!

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