在Next.js 16.2.6和 Framer Motion 12.4.0下,退出动画不起作用

前端开发 2026-07-09

我正在使用Framer Motion v12.40.0尝试为每个页面实现页面切换过渡。我的目标是当用户点击导航栏中的链接时,当前页面先执行退出动画,然后跳转到下一页。

但我遇到了一个问题:退出动画不起作用,只有进入动画起作用。

我的思路是写一个animate包装器,然后将其应用到每个页面。

以下是导航栏的完整代码:

import Link from 'next/link'

export default function Navbar() {
  return (
    <nav className="bg-orange-950 text-gray-50 p-4 shadow-md">
      <div className="container mx-auto flex justify-between items-center">
        <Link href="/" className="text-xl font-bold">
          Company Logo
        </Link>
        <div className="space-x-6">
          <Link href="/" className="hover:text-lime-600 transition">
            Home
          </Link>
          <Link href="/services" className="hover:text-lime-600 transition">
            Services
          </Link>
          <Link href="/pricing" className="hover:text-lime-600 transition">
            Pricing
          </Link>
          <Link href="/about" className="hover:text-lime-600 transition">
            About
          </Link>
          <Link href="/contact" className="hover:text-lime-600 transition">
            Contact
          </Link>
        </div>
      </div>
    </nav>
  )
}

以下是 layout.tsx 的完整代码:

import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import Navbar from '@/components/Navbar'
import Footer from '@/components/Footer'
import './globals.css'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Company Website',
  description: 'Welcome to our company website',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <div className="min-h-screen flex flex-col">
          <Navbar />
          <main className="w-full">
            {children}
          </main>
          <Footer />
        </div>
      </body>
    </html>
  )
}

以下是客户端组件中animate包装器的完整代码:

'use client';

import { motion, AnimatePresence } from 'framer-motion';
import { usePathname } from "next/navigation";

export default function AnimateWrapper({ children }: { children: React.ReactNode }) {
  const pathname = usePathname();

  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={pathname}
        initial={{ opacity: 0, scale: 0.95, x:-1000 }}
        animate={{ opacity: 1, scale: 1, x:0 }}
        exit={{ opacity: 0, scale: 0.95, y:100 }}
        transition={{ duration: 0.7, ease: [0.25, 0.1, 0.25, 1] }}
      >
        {children}
      </motion.div>
    </AnimatePresence>
  );
}

以下是首页的完整代码,我将animate包装器应用到包括首页在内的每段代码:

import AnimateWrapper from '@/components/AnimateWrapper';

export default function Home() {
  return (
    <AnimateWrapper>
      <div className="w-full justify-items-center p-10 min-h-[calc(100vh-8rem)] bg-green-950 text-blue-100">
        <h1 className="text-4xl font-bold mb-6">Welcome to our company</h1>
        <div className="text-lg">
           ...
        </div>
      </div>
    </AnimateWrapper>
  )
}

我已经阅读了官方文档,animate包装器中的motion.div标签需要一个key属性来启用退出动画,但我不知道key属性应使用的正确值,或者是否还缺少什么。

先感谢你们提供的任何解决方案和回答。

解决方案

根本原因在于Next.js App路由架构与传统页面路由相比已发生了根本变化。

有两种可能有帮助的方法。

第一种方法是把你的Next.js项目切换回使用传统的页面路由并重新启动。

Next.js仍然允许用户在最新版本中选择页面路由。

如果你坚持使用App路由,那么有一个名为next-transition-router的库。

https://github.com/ismamz/next-transition-router

下面是我在GitHub仓库中的测试网站。

https://github.com/robin-artemstein/nextjs-company-website-framer-motion

祝你好运。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章