You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Framer Motion子元素stagger动画失效问题求助

Framer Motion父元素Stagger动画不生效的解决方案

核心问题分析

你的代码里有两个关键问题导致父元素的stagger配置失效:

  • 子元素手动声明了initial="initial"和animate="open",完全脱离了父元素的变体控制,父元素的stagger规则无法传递给子元素
  • 父元素未配置exit状态,关闭时的反向stagger效果无法触发

具体修改步骤

  1. 移除子元素的手动initial/animate属性
    子元素只需要绑定variants,让父元素统一控制动画状态,这样父元素的stagger配置才能生效:

    // 原代码
    <motion.div
      key={index}
      variants={childVars}
      initial="initial"
      animate="open"
    >
    // 修改后
    <motion.div
      key={index}
      variants={childVars}
    >
    
  2. 给父元素补充exit变体配置
    为父元素的parentVars添加exit状态,实现关闭时的反向stagger效果:

    const parentVars = {
      initial: {
        transition: {
          staggerChildren: 0.2,
          staggerDirection: -1
        }
      },
      open: {
        transition: {
          delayChildren: 0.3,
          staggerChildren: 0.2,
          staggerDirection: 1
        }
      },
      exit: {
        transition: {
          staggerChildren: 0.15,
          staggerDirection: -1
        }
      }
    };
    
  3. 同步父元素的exit状态绑定
    把父级motion.div的exit属性改为"exit",对应上面新增的变体:

    <motion.div
      className="flex flex-col items-center justify-center h-full"
      variants={parentVars}
      initial="initial"
      animate="open"
      exit="exit"
    >
    
  4. 优化菜单容器的动画时序
    给menuVars添加透明度动画,并调整过渡参数,让菜单高度变化和子元素动画更协调:

    const menuVars = {
      initial: { height: 0, opacity: 0 },
      animate: {
        height: "15rem",
        opacity: 1,
        transition: {
          duration: 0.5,
          ease: "easeOut"
        }
      },
      exit: {
        height: 0,
        opacity: 0,
        transition: {
          duration: 0.5,
          ease: "easeIn",
          delayChildren: 0.3
        }
      }
    };
    

修改后的完整代码

import { useState } from "react";
import CarouselP1 from "../carousel/carouselP1";
import { MdKeyboardDoubleArrowDown } from "react-icons/md";
import { MdKeyboardDoubleArrowUp } from "react-icons/md";
import { motion, AnimatePresence } from "framer-motion";

const ProjectCard1 = ({ images }) => {
  const [expanded, setExpanded] = useState(false);

  const handleExpand = () => {
    setExpanded(!expanded);
  };

  const details = [
    { text: "afafcacascasc" },
    { text: "adasdascasc" },
    { text: "afafcacawfgwefaEDFAscasc" },
    { text: "afafcaE5TWERWEcascasc" },
    { text: "DFWEQFEAFA" },
    { text: "HRTHDGDRG" },
  ];

  const menuVars = {
    initial: { height: 0, opacity: 0 },
    animate: {
      height: "15rem",
      opacity: 1,
      transition: {
        duration: 0.5,
        ease: "easeOut"
      }
    },
    exit: {
      height: 0,
      opacity: 0,
      transition: {
        duration: 0.5,
        ease: "easeIn",
        delayChildren: 0.3
      }
    }
  };

  const parentVars = {
    initial: {
      transition: {
        staggerChildren: 0.2,
        staggerDirection: -1
      }
    },
    open: {
      transition: {
        delayChildren: 0.3,
        staggerChildren: 0.2,
        staggerDirection: 1
      }
    },
    exit: {
      transition: {
        staggerChildren: 0.15,
        staggerDirection: -1
      }
    }
  };

  const childVars = {
    initial: {
      y: "30vh",
      transition: { duration: 0.5 }
    },
    open: {
      y: 0,
      transition: { duration: 0.7 }
    },
    exit: {
      y: "30vh",
      transition: { duration: 0.5 }
    }
  };

  return (
    <div className="flex items-center justify-center flex-col space-y-3">
      <CarouselP1 images={images} />
      <div className="flex flex-col items-center justify-center w-96 max-w-80">
        <p>WEGdrfbxcvb</p>
      </div>
      <button
        className="flex items-center justify-center cursor-pointer"
        onClick={handleExpand}
      >
        <span className="pr-2">Details</span>
        {expanded ? (
          <MdKeyboardDoubleArrowUp className="mt-1" />
        ) : (
          <MdKeyboardDoubleArrowDown className="mt-1" />
        )}
      </button>
      <AnimatePresence>
        {expanded && (
          <motion.div
            variants={menuVars}
            initial="initial"
            animate="animate"
            exit="exit"
            className="flex rounded-lg items-center h-[15rem] justify-center w-48 bg-sky-200 my-2 origin-top overflow-hidden"
          >
            <motion.div
              className="flex flex-col items-center justify-center h-full"
              variants={parentVars}
              initial="initial"
              animate="open"
              exit="exit"
            >
              {details.map((item, index) => (
                <div className="overflow-hidden" key={index}>
                  <motion.div variants={childVars}>
                    {item.text}
                  </motion.div>
                </div>
              ))}
            </motion.div>
          </motion.div>
        )}
      </AnimatePresence>

      <button className="w-18 h-10 p-2 bg-sky-400 rounded-lg">
        <a href="">Source Code</a>
      </button>
    </div>
  );
};

export default ProjectCard1;

内容的提问来源于stack exchange,提问作者MH_Rabea

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 03:43:24