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

问题描述

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

enter image description here

当我点击关闭菜单图标时,整个侧边抽屉向左滑出(这意味着 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",};

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 },};

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;

解决方法

你需要告诉 side drawer 等到孩子们的退出动画结束,然后再开始自己的退出动画。

您可以使用 whentransition 属性执行此操作。请参阅文档中的 Orchestration

在您的情况下,您可以将它添加到您的 initialdrawerVariants 变体中,因为这是您在退出时动画化的变体:

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

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