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 && ...},完全没必要。
正确的做法是:
- 把AnimatePresence移到条件渲染外面,让它始终存在于DOM中
- 给内部的motion.div设置唯一且稳定的key(这里用固定字符串就行,比如
"details-panel") - 只在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
相关产品推荐
相关产品推荐

