如何在Framer Motion中结合使用AnimatePresence与AnimatedSharedLayout
问题解决方案
核心问题点
AnimatePresence的触发逻辑是识别其直接子元素的key属性变化,你当前将key绑定在了AnimatePresence组件本身,子元素没有唯一标识,组件无法感知内容切换,因此不会触发退场动画。- 内容容器没有添加
layout属性,无法和父容器的布局动画联动,高度无法平滑过渡。 - 入场动画的1.2s延迟设置不合理,和
exitBeforeEnter的衔接逻辑冲突,会导致动画时序混乱。
调整后完整代码
import { useState } from "react"; import { motion, AnimatePresence, AnimateSharedLayout } from "framer-motion"; import "./styles.css"; function Home() { return ( <div> <div className="row" /> <div className="row" /> </div> ); } function Contact() { return ( <div> <div className="row" /> <div className="row" /> <div className="row" /> <div className="row" /> <div className="row" /> </div> ); } const sections = { home: "Home", contact: "Contact" }; export default function App() { const [activeSection, setActiveSection] = useState(sections.home); return ( <div className="outer-container"> <AnimateSharedLayout> <motion.div layout initial={{ borderRadius: 25 }}> <h3>顶部文本</h3> <AnimatePresence exitBeforeEnter initial={false}> <motion.div key={activeSection} layout initial={{ opacity: 0 }} animate={{ opacity: 1, transition: { delay: 0.2, duration: 0.3 } }} exit={{ opacity: 0, transition: { duration: 0.3 } }} > <div className="inner-container"> <div className="content"> <div className="avatar" /> {activeSection === sections.home && <Home />} {activeSection === sections.contact && <Contact />} </div> </div> </motion.div> </AnimatePresence> </motion.div> </AnimateSharedLayout> <button type="button" onClick={() => setActiveSection( activeSection === sections.home ? sections.contact : sections.home ) } > 前往{activeSection === sections.home ? "Contact" : "Home"}板块 </button> </div> ); }
效果对应说明
- 旧子容器先触发exit透明度动画,0.3s完成渐隐
- 旧元素完全退场后,父容器触发布局动画,0.2s平滑过渡到新内容的高度
- 高度过渡完成后,新子容器触发入场透明度动画,0.3s完成渐入
完全匹配你需要的三步效果。
内容的提问来源于stack exchange,提问作者zeckdude
相关产品推荐
相关产品推荐

