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

Framer Motion退出动画失效:如何设置唯一稳定Key?

Framer Motion exit动画失效问题解决

我写的代码里Framer Motion的exit动画不生效,按照官方文档说的,AnimatePresence的直接子元素需要设置唯一且稳定的key,但我试了用字符串当key也没用,请问这种场景下该怎么正确设置?

相关代码:

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);
  };
  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 className=" "></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>
      {expanded && (
        <AnimatePresence>
        {expanded && (
          <motion.div
            
            initial={{ height: 0, opacity: 0 }}
            animate={{ height: "15rem", opacity: 1 }}
            exit={{ height:'0', opacity: '0' }}
            transition={{duration: 0.6, ease: "easeInOut"}}
            className="flex rounded-lg items-center justify-center w-48 bg-sky-200"
          >
            <ul>
              <li>1</li>
              <li>2</li>
              <li>3</li>
              <li>4</li>
              <li>5</li>
              <li>6</li>
            </ul>
          </motion.div>
        )}
      </AnimatePresence>)}


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

export default ProjectCard1;

解决方法

你这代码的核心问题是AnimatePresence被包在了条件渲染里——当expanded变为false时,AnimatePresence本身会被直接卸载,根本没机会触发子元素的exit动画。另外你嵌套了两层{expanded && ...},完全没必要。

正确的做法是:

  1. 把AnimatePresence移到条件渲染外面,让它始终存在于DOM中
  2. 给内部的motion.div设置唯一且稳定的key(这里用固定字符串就行,比如"details-panel")
  3. 只在AnimatePresence内部用expanded控制motion.div的显示

修改后的完整代码:

import CarouselP1 from "../carousel/carouselP1";
import { MdKeyboardDoubleArrowDown } from "react-icons/md";
import { MdKeyboardDoubleArrowUp } from "react-icons/md";
import { motion, AnimatePresence} from "framer-motion";
import { useState } from "react"; // 补全之前遗漏的useState导入

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

  const handleExpand = () => {
    setExpanded(!expanded);
  };
  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 className=" "></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移到外部,保持始终渲染 */}
      <AnimatePresence>
        {expanded && (
          <motion.div
            key="details-panel" // 设置唯一稳定的key
            initial={{ height: 0, opacity: 0 }}
            animate={{ height: "15rem", opacity: 1 }}
            exit={{ height: 0, opacity: 0 }}
            transition={{ duration: 0.6, ease: "easeInOut" }}
            className="flex rounded-lg items-center justify-center w-48 bg-sky-200 overflow-hidden" // 增加overflow:hidden避免内容溢出
          >
            <ul>
              <li>1</li>
              <li>2</li>
              <li>3</li>
              <li>4</li>
              <li>5</li>
              <li>6</li>
            </ul>
          </motion.div>
        )}
      </AnimatePresence>

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

export default ProjectCard1;

额外补充:我帮你补上了之前遗漏的useState导入,还给motion.div加了overflow:hidden,这样在height变为0时内部列表不会外露,动画效果更自然。


内容的提问来源于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 00:56:28