退出在 Framer Motion 中不起作用的子项目的动画

Exit animation for children items not working in Framer Motion

我正在使用 Framer Motion 在 React 中制作汉堡菜单动画。当我点击汉堡包菜单时,侧边抽屉和导航项从左侧滑入。

当我点击关闭菜单图标时,整个侧边抽屉向左滑出(这意味着 SideDrawer 组件上的 exit prop 正在工作)。

我想要什么? 当我点击关闭图标时,我希望导航项先滑出,然后是侧边抽屉。我尝试将 exit 道具添加到子导航项中。但是没用。

怎样才能达到想要的效果?

代码片段如下:

src/App.js

import React, { useState } from "react";
import "./App.css";
import Menu from "./components/Menu";
import SideDrawer from "./components/SideDrawer";
import Overlay from "./components/Overlay";

const App = () => {
  const [menuOpen, setMenuOpen] = useState(false);
  const handleMenuClick = () => {
    setMenuOpen(!menuOpen);
  };

  return (
    <div className="App">
      <Menu menuOpen={menuOpen} onMenuClick={handleMenuClick} />
      <SideDrawer menuOpen={menuOpen} />
      <Overlay menuOpen={menuOpen} />
    </div>
  );
};

export default App;

src/components/Menu.js

import React, { useState } from "react";
import { motion } from "framer-motion";

const lineOneVariants = {
  initial: { rotate: "0deg" },
  animate: { y: ".8rem", rotate: "45deg", transformOrigin: "center center" },
};
const lineTwoVariants = {
  initial: { opacity: 1 },
  animate: { opacity: 0 },
};
const lineThreeVariants = {
  initial: { rotate: "0deg" },
  animate: { y: "-.8rem", rotate: "-45deg", transformOrigin: "center center" },
};

const Menu = ({ onMenuClick, menuOpen }) => {
  return (
    <div className="hamburger_menu">
      <div className="hamburger_menu-line-container" onClick={onMenuClick}>
        <motion.div
          variants={lineOneVariants}
          initial="initial"
          animate={menuOpen ? "animate" : "initial"}
          className="hamburger_menu-line-1"
        ></motion.div>
        <motion.div
          variants={lineTwoVariants}
          initial="initial"
          animate={menuOpen ? "animate" : "initial"}
          className="hamburger_menu-line-2"
        ></motion.div>
        <motion.div
          variants={lineThreeVariants}
          initial="initial"
          animate={menuOpen ? "animate" : "initial"}
          className="hamburger_menu-line-3"
        ></motion.div>
      </div>
    </div>
  );
};

export default Menu;

src/components/Overlay.js

import React from "react";
import { motion } from "framer-motion";

const overlayVariants = {
  initial: { opacity: 0 },
  animate: { opacity: 1 },
};

const Overlay = ({ menuOpen }) => {
  return (
    <motion.div
      variants={overlayVariants}
      initial="initial"
      animate={menuOpen ? "animate" : "initial"}
      className="overlay"
    ></motion.div>
  );
};

export default Overlay;

src/components/SideDrawer.js

import React from "react";
import { motion, AnimatePresence } from "framer-motion";

const drawerVariants = {
  initial: {
    x: "-100vw",
    opacity: 0,
  },
  animate: {
    x: 0,
    opacity: 1,
    transition: {
      type: "linear",
      ease: "easeInOut",
      staggerChildren: 0.1,
      delayChildren: 0.15,
    },
  },
};

const drawerMenuVariants = {
  initial: { x: "-5rem", opacity: 0 },
  animate: {
    x: 0,
    opacity: 1,
    transition: {
      type: "linear",
      ease: "easeInOut",
    },
  },
};

const SideDrawer = ({ menuOpen }) => {
  return (
    <AnimatePresence>
      {menuOpen && (
        <motion.div
          variants={drawerVariants}
          initial="initial"
          animate="animate"
          exit="initial"
          className="sideDrawer"
        >
          <nav className="sideDrawer-menu">
            <ul>
              <motion.li
                variants={drawerMenuVariants}
                whileHover={{ scale: 1.05 }}
              >
                <a href="#">Register/Login</a>
              </motion.li>
              <motion.li
                variants={drawerMenuVariants}
                whileHover={{ scale: 1.05 }}
              >
                <a href="#">About</a>
              </motion.li>
              <motion.li
                variants={drawerMenuVariants}
                whileHover={{ scale: 1.05 }}
              >
                <a href="#">Projects</a>
              </motion.li>
              <motion.li
                variants={drawerMenuVariants}
                whileHover={{ scale: 1.05 }}
              >
                <a href="#">CV</a>
              </motion.li>
            </ul>
          </nav>
        </motion.div>
      )}
    </AnimatePresence>
  );
};

export default SideDrawer;

您需要告诉侧抽屉等到子项的退出动画结束后再开始自己的退出动画。

您可以使用 transitionwhen 属性 来完成此操作。请参阅文档中的 Orchestration

在您的情况下,您会将其添加到 drawerVariantsinitial 变体中,因为这是您在退出时设置动画的变体:

initial: {
  x: "-100vw",
  opacity: 0,
  transition: {
    when: "afterChildren"
  }
},

如果你想在行为中反映动画,你可能还想在那里添加一些惊人的缓动,但我会把它留给你。