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

如何在NextJS中实现多路由页面间的组件滑动转场?

在Next.js中实现跨路由的滑动转场效果

要实现类似DigitalOcean那种两个独立路由间的向右滑动转场,核心是利用Next.js的路由事件监听+CSS动画,以下分两种路由方案给出实现步骤:

一、App Router(Next.js 13+)实现方式

1. 定义全局动画样式

在app/globals.css中添加转场动画的CSS规则:

/* 页面容器:确保占满视口,避免滚动干扰 */
.page-container {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  min-height: 100vh;
}

/* 从右侧滑入的进入动画 */
.slide-in-right {
  animation: slideInRight 0.3s ease-out forwards;
}

/* 向左侧滑出的离开动画 */
.slide-out-left {
  animation: slideOutLeft 0.3s ease-in forwards;
}

/* 反向路由的动画(从邮箱页返回首页) */
.slide-in-left {
  animation: slideInLeft 0.3s ease-out forwards;
}
.slide-out-right {
  animation: slideOutRight 0.3s ease-in forwards;
}

@keyframes slideInRight {
  from { transform: translateX(100%); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

@keyframes slideOutLeft {
  from { transform: translateX(0); opacity: 1; }
  to { transform: translateX(-100%); opacity: 0; }
}

@keyframes slideInLeft {
  from { transform: translateX(-100%); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

@keyframes slideOutRight {
  from { transform: translateX(0); opacity: 1; }
  to { transform: translateX(100%); opacity: 0; }
}

2. 在根布局中添加路由过渡逻辑

修改app/layout.tsx,通过usePathname监听路由变化,配合Transition组件(需安装react-transition-group)添加动画类:

import { Transition } from 'react-transition-group';
import { usePathname } from 'next/navigation';
import './globals.css';
import { useEffect, useRef } from 'react';

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

  // 记录上一次路由路径
  useEffect(() => {
    prevPathname.current = pathname;
  }, [pathname]);

  // 判断路由跳转方向
  const isToEmail = prevPathname.current === '/registrations/new' && pathname === '/registrations/email';
  const isToNew = prevPathname.current === '/registrations/email' && pathname === '/registrations/new';

  return (
    <html lang="zh-CN">
      <body style={{ margin: 0, overflow: 'hidden' }}>
        <Transition
          in={true}
          timeout={300}
          mountOnEnter
          unmountOnExit
        >
          {(state) => {
            let animationClass = '';
            if (state === 'entering') {
              animationClass = isToEmail ? 'slide-in-right' : isToNew ? 'slide-in-left' : '';
            } else if (state === 'exiting') {
              animationClass = isToEmail ? 'slide-out-left' : isToNew ? 'slide-out-right' : '';
            }
            return <div className={`page-container ${animationClass}`}>{children}</div>;
          }}
        </Transition>
      </body>
    </html>
  );
}

3. 创建路由页面

分别创建app/registrations/new/page.tsx和app/registrations/email/page.tsx,用Link组件实现跳转:

// app/registrations/new/page.tsx
import Link from 'next/link';

export default function NewRegistrationPage() {
  return (
    <div style={{ padding: '2rem' }}>
      <h1>欢迎注册</h1>
      <Link href="/registrations/email" style={{ fontSize: '1.2rem' }}>使用邮箱注册</Link>
    </div>
  );
}
// app/registrations/email/page.tsx
import Link from 'next/link';

export default function EmailRegistrationPage() {
  return (
    <div style={{ padding: '2rem' }}>
      <h1>邮箱注册</h1>
      <form>
        <input type="email" placeholder="输入邮箱" style={{ padding: '0.5rem', fontSize: '1rem' }} />
        <button type="submit" style={{ marginLeft: '1rem', padding: '0.5rem 1rem' }}>下一步</button>
      </form>
      <Link href="/registrations/new" style={{ display: 'block', marginTop: '1rem' }}>返回</Link>
    </div>
  );
}

二、Pages Router(Next.js 12及以下)实现方式

1. 定义全局动画样式

在styles/globals.css中添加和App Router一致的动画规则(同上)。

2. 在_app.tsx中监听路由变化

修改pages/_app.tsx,通过useRouter的事件监听来切换动画类:

import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import '../styles/globals.css';

function MyApp({ Component, pageProps }) {
  const router = useRouter();
  const [animationClass, setAnimationClass] = useState('');

  useEffect(() => {
    // 路由开始跳转时添加离开动画
    const handleRouteStart = (url) => {
      const targetPath = url.split(window.location.origin)[1];
      if (router.pathname === '/registrations/new' && targetPath === '/registrations/email') {
        setAnimationClass('slide-out-left');
      } else if (router.pathname === '/registrations/email' && targetPath === '/registrations/new') {
        setAnimationClass('slide-out-right');
      }
    };

    // 路由跳转完成后添加进入动画
    const handleRouteComplete = () => {
      if (router.pathname === '/registrations/email') {
        setAnimationClass('slide-in-right');
      } else if (router.pathname === '/registrations/new') {
        setAnimationClass('slide-in-left');
      }
      // 动画结束后清除类名
      setTimeout(() => setAnimationClass(''), 300);
    };

    router.events.on('routeChangeStart', handleRouteStart);
    router.events.on('routeChangeComplete', handleRouteComplete);

    return () => {
      router.events.off('routeChangeStart', handleRouteStart);
      router.events.off('routeChangeComplete', handleRouteComplete);
    };
  }, [router]);

  return (
    <div className={`page-container ${animationClass}`}>
      <Component {...pageProps} />
    </div>
  );
}

export default MyApp;

3. 创建路由页面

在pages/registrations/目录下创建new.tsx和email.tsx,用Link组件实现跳转(代码和App Router的页面类似)。

优化提示

  • 可以调整动画时长(比如0.3s)和缓动函数(比如ease-out)来匹配目标效果;
  • 如果需要更复杂的转场逻辑,可使用framer-motion库替代原生CSS动画;
  • 确保页面容器的overflow: hidden,避免动画过程中出现滚动条。

内容的提问来源于stack exchange,提问作者Yes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 01:20:34