Jump to content
Search Community

Timeline wont reverse (React)

connor.online test
Moderator Tag

Recommended Posts

Hi all,

I can't seem to get my timeline to reverse on a menu component using React. It seems the animation is just resetting to its previous value rather than animating back to it. I also have a suspicion that perhaps I'm creating a new timeline each time rather than reusing the same one, but I can't figure out how to adapt what I'm reading to my use case.

 

Thanks for your time,

Connor

 

import React, { useEffect, useState, useRef } from 'react';
import { NavLink } from 'react-router-dom';
import './index.css';
import gsap from 'gsap';
import {TextPlugin} from 'gsap/TextPlugin';
gsap.registerPlugin(TextPlugin);

function Menu() {

    // Ref Declarations
    const logoRef = useRef();
    const iconRef = useRef();
    const dropDownRef = useRef();
    const menuItemsRef = useRef([]);
    const linkRef = useRef([]);
    const menuTimeline = useRef();

    const [menuOpen, setMenuOpen] = useState(false);

    // NavBar Loading Animation
    useEffect(() => {
        gsap.fromTo([logoRef.current], {
            text: '',
            opacity: 0,
        },
        {
            text: 'connor white',
            opacity: 1,
            duration: 2,
            ease: "power4.inOut"
        })
        gsap.fromTo([iconRef.current], {
            x: 75,
            rotateZ: 360
        },
        {
            x: 0,
            rotateZ: 0,
            duration: 1,
            ease: "power3.inOut",
            delay: .75
        })
    }, []);

    // Menu State Change
    const toggleMenu = () => {
        setMenuOpen(!menuOpen);
    }

    // Menu Timeline Animation
    menuTimeline.current = gsap.timeline({ paused: true });
    menuTimeline.current.fromTo(dropDownRef.current, {
        opacity: 0,
        display: 'none',
        // backgroundColor: 'transparent',
        height: '0vh',
        width: '0vw',
    }, {
        opacity: 1,
        display: 'block',
        backgroundColor: '#000000',
        height: '100vh',
        width: '100vw',
        duration: .25,
        ease: "power4.inOut"
    }, 0);
    menuTimeline.current.fromTo(iconRef.current, {
        rotateZ: 0
    }, {
        rotateZ: 315,
        duration: .75,
        ease: "back.inOut"
    }, .25);
    menuTimeline.current.fromTo(logoRef.current, {
        text: {
            value: 'connor white',
        }
    },{
        duration: 1,
        text: {
            value: 'designer & developer',
            ease: "power4.inOut"
        }, 
    }, .25);
    menuTimeline.current.fromTo(menuItemsRef.current.children, {
        opacity: 0,
        x: 25,
    }, {
        opacity: 1,
        x: 0,
        duration: .25,
        ease: "power4.inOut",
        delay: .25,
        stagger: {
            amount: .25
        }
    }, .25);
    menuTimeline.current.fromTo(linkRef.current.children, {
        scale: 0,
        opacity: 0,
    }, {
        scale: 1,
        opacity: 1,
        duration: .5,
        ease: "back.inOut",
        stagger: {
            amount: .25,
            from: 'end'
        }
    }, .5);

    // Run menu timeline animation when menu state changes
    useEffect(() => {

        let ctx = gsap.context(() => {
            menuOpen ? menuTimeline.current.play() : menuTimeline.current.reversed();
        })

        return () => ctx.revert();

    }, [setMenuOpen, menuOpen]);
    

    return (
        <div className="menu">
            <div className="navbar">
                <div className="name" ref={logoRef}>connor white</div>
                <div >
                    <img onClick={toggleMenu} ref={iconRef} src="./media/icons/menu-button.svg" alt="menu button" className="menu-icon"/>
                </div>
            </div>
            <div className="dropdown" ref={dropDownRef}>
                <div ref={menuItemsRef} className="menu-items"> 
                        <NavLink to="/" className="nav-link" onClick={toggleMenu}>Intro</NavLink>
                        <NavLink to="/projects" className="nav-link" onClick={toggleMenu}>Projects</NavLink>
                        <NavLink to="/contact" className="nav-link" onClick={toggleMenu}>Contact</NavLink>
                        <div ref={linkRef} className="linkouts"> 
                            <a href="https://www.linkedin.com/in/connorwhite-online/" target={"_blank"} rel="noreferrer"><img src="./media/icons/linkedin.svg" alt="Connor's LinkedIn" className="social-links" /></a>
                            <a href="https://github.com/connorwhite-online" target={"_blank"} rel="noreferrer"><img src="./media/icons/github.svg" alt="Connor's Github" className="social-links" /></a>
                            <a href="https://twitter.com/connor_online" target={"_blank"} rel="noreferrer"><img src="./media/icons/twitter.svg" alt="Connor's Twitter" className="social-links" /></a>
                            <a href="https://instagram.com/connorwhite.online" target={"_blank"} rel="noreferrer"><img src="./media/icons/instagram.svg" alt="Connor's Instagram" className="social-links" /></a>
                        </div>
                </div>
            </div>
        </div>
    )
}

export default Menu;

 

Link to comment
Share on other sites

Really difficult to debug something like this without an example.

One thing you could do is instead define tweens to scrub your timeline

 

const closeMenu = () => {
  gsap.to(menuTimeline.current, {
	progress: 0
})
}

const openMenu = () => {
	gsap.to(menuTimeline.current, {
	progress: 1
})
}

Also, you don't have to rewrite the tl name when writing many tweens to it:

 

 menuTimeline.current.fromTo(iconRef.current, {
        rotateZ: 0
    }, {
        rotateZ: 315,
        duration: .75,
        ease: "back.inOut"
    }, .25).fromTo(logoRef.current, {
        text: {
            value: 'connor white',
        }
    },{
        duration: 1,
        text: {
            value: 'designer & developer',
            ease: "power4.inOut"
        }, 
    }, .25).fromTo(...)

 

Link to comment
Share on other sites

Hi Connor,

 

A few things that should be important and perhaps will help beyond the great suggestions by Steve.

 

First you have a GSAP syntax issue:

// Wrong
menuOpen ? menuTimeline.current.play() : menuTimeline.current.reversed();

// Right
menuOpen ? menuTimeline.current.play() : menuTimeline.current.reverse();

// Another alternative
menuTimeline.current.reversed(!menuOpen);

Second, GSAP context is not the place to toggle/play/reverse a GSAP instance, specially in a React environment, that is ideal to revert all instances before the component unmounts and to avoid some unexpected behaviour on development due to React's strict mode.

 

Third, is not recommended to replace the current value on a ref on every re-render. Right now you are replacing menuTimeline.current on every re-render, since it's in the root scope of your functional component:

function Menu() {
  const menuTimeline = useRef();
  
  // This runs on every re-render
  menuTimeline.current = gsap.timeline();
  menuTimeline.current
    .to()
  	.to();
  return (
    /* ... */
  );
}

If your timeline is going to run multiple times but you never need to change it's parameters or the target values, then there is no need to create said timeline over and over again, just create the timeline once in the first useEffect hook and then toggle it when the specific state is updated. Since you use a ref for the timeline it'll be kept through re-renders:

function Menu() {
  const menuTimeline = useRef();
  
  useEffect(() => {
    menuTimeline.current = gsap.timeline({ paused: true });
    const ctx = gsap.context(() => {      
      menuTimeline.current
        .to()
        .to()
      	.reverse();
    })
    return () => {
      ctx.revert();
    };
  }, []);
  
  useEffect(() => {
    menuTimeline.current.reversed(!menuOpen);
  }, [setMenuOpen, menuOpen]);
  
  return (
    /* ... */
  );

Finally take a look at the React guide in the blog:

Happy Tweening!!!

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