首页 > 解决方案 > 在 Framer Motion 中不起作用的子项的退出动画

问题描述

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

当我单击关闭菜单图标时,整个侧抽屉滑到左侧(这意味着 SideDrawer 组件上的退出道具正在工作)。

我想要什么? 当我单击关闭图标时,我希望导航项先滑出,然后再滑出侧抽屉。我尝试将退出道具添加到儿童导航项。但它不起作用。

我怎样才能达到预期的效果?

代码片段如下:

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;

标签: reactjsanimationframer-motionreact-animations

解决方案


您需要告诉侧抽屉等到孩子的退出动画完成,然后再开始自己的退出动画。

您可以通过使用when. transition请参阅文档中的编排

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

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

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


推荐阅读