Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

React Link framer motion animation with AnimatePresence

I have a Navigation component in which the Menu Items float in separately on load and float out on click.
When I added Router and changed the items to Links, the exit animation didn’t work because it loaded the new Route component right away.

I want to keep the items individual animation with Link functionality.

Here is the link:
https://codesandbox.io/s/elastic-leaf-fxsswo?file=/src/components/Navigation.js

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Code:

export const Navigation = () => {
  const navRef = useRef(null);

  const onResize = () => {
    setIsColumn(window.innerWidth <= 715);
  };

  const [clickOnMenu, setClick] = useState(false);
  const [itemtransition, setTransition] = useState(
    Array(menuItems.length).fill(0)
  );
  const [isColumn, setIsColumn] = useState(window.innerWidth <= 715);

  const click = (e) => {
    const copy = [...itemtransition];
    const index = e.target.id;
    setTransition(copy.map((e, i) => (Math.abs(index - i) + 1) / 10));
    setTimeout(() => setClick(true), 50);
  };

  useEffect(() => {
    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
  }, []);

  return (
    <AnimatePresence exitBeforeEnter>
      {!clickOnMenu && (
        <Nav ref={navRef}>
          {menuItems.map((e, i) => {
            const text = Object.keys(e)[0];
            const value = Object.values(e)[0];
            return (
              <Item
                id={i}
                key={value}
                animate={{
                  x: 0,
                  y: 0,
                  opacity: 1,
                  transition: { delay: (i + 1) / 10 }
                }}
                initial={{
                  x: isColumn ? 1000 : 0,
                  y: isColumn ? 0 : 1000,
                  opacity: 0
                }}
                exit={{
                  x: isColumn ? -1000 : 0,
                  y: isColumn ? 0 : -1000,
                  opacity: 0,
                  transition: { delay: itemtransition[i] }
                }}
                onClick={click}
              >
                {/*<Link to={`/${value}`}>{text}</Link>*/}
                {text}
              </Item>
            );
          })}
        </Nav>
      )}
    </AnimatePresence>
  );
};

In the sandbox in Navigation.js 69-70. row:

This is the desired animation.

69. {/*<Link to={`/${value}`}>{text}</Link>*/}
70. {text}

But when I use Link there is no exit animation

69. <Link to={`/${value}`}>{text}</Link>
70. {/*text*/}

Is there a workaround or I should forget router-dom.

Thank you in forward!

>Solution :

This may be a bit hackish, but with routing and transitions sometimes that is the nature. I suggest rendering the Link so the semantic HTML is correct and add an onClick handler to prevent the default navigation action from occurring. This allows any transitions/animations to go through. Then update the click handler of the Item component to consume the link target and issue an imperative navigation action on a timeout to allow transitions/animations to complete.

I used a 750ms timeout but you may need to tune this value to better suit your needs.

Example:

...
import { Link, useNavigate } from "react-router-dom";

...

export const Navigation = () => {
  const navRef = useRef(null);
  const navigate = useNavigate(); // <-- access navigate function

  ...

  const click = target => (e) => { // <-- consume target
    const copy = [...itemtransition];
    const index = e.target.id;
    setTransition(copy.map((e, i) => (Math.abs(index - i) + 1) / 10));
    setTimeout(() => {
      setClick(true);
    }, 50);
    setTimeout(() => {
      navigate(target); // <-- navigate after some delta
    }, 750);
  };

  ...

  return (
    <AnimatePresence exitBeforeEnter>
      {!clickOnMenu && (
        <Nav ref={navRef}>
          {menuItems.map((e, i) => {
            const text = Object.keys(e)[0];
            const value = Object.values(e)[0];
            return (
              <Item
                ...
                onClick={click(`/${value}`)} // <-- pass target to handler
              >
                <Link
                  to={`/${value}`}
                  onClick={e => e.preventDefault()} // <-- prevent link click
                >
                  {text}
                </Link>
              </Item>
            );
          })}
        </Nav>
      )}
    </AnimatePresence>
  );
};

...

Edit react-link-framer-motion-animation-with-animatepresence

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading