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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 22:15:02