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

NextJS App Router+GSAP作品集:预加载器与页面过渡实现咨询

解决方案:Next.js App Router + GSAP 预加载器与页面过渡实现

一、优质预加载器实现

核心思路

抛弃固定延迟,结合页面实际加载状态(路由切换pending状态、数据/资源加载完成信号)+ GSAP动态动画,实现流畅的加载体验。

代码实现示例

在template.tsx中:

'use client';
import { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import gsap from 'gsap';

export default function Template({ children }: { children: React.ReactNode }) {
  const [showPreloader, setShowPreloader] = useState(true);
  const router = useRouter();

  useEffect(() => {
    // 初始化预加载器入场动画
    const preloaderTl = gsap.timeline();
    preloaderTl.from('.preloader__progress', { scaleX: 0, duration: 1, ease: 'power2.out' });

    // 监听路由切换前后状态
    const handleRouteChange = () => {
      setShowPreloader(true);
      // 重置进度条动画
      gsap.to('.preloader__progress', { scaleX: 0, duration: 0 });
      gsap.from('.preloader__progress', { scaleX: 0, duration: 0.8, ease: 'power2.out' });
    };

    const handleRouteComplete = () => {
      // 预加载器退场动画
      gsap.to('.preloader', { opacity: 0, y: -100, duration: 0.5, ease: 'power2.in', onComplete: () => setShowPreloader(false) });
    };

    router.beforeNavigate(handleRouteChange);
    router.afterNavigate(handleRouteComplete);

    // 首次加载完成后自动隐藏
    window.addEventListener('load', () => {
      setTimeout(handleRouteComplete, 500); // 留缓冲时间展示动画
    });

    return () => {
      router.beforeNavigate(null);
      router.afterNavigate(null);
      window.removeEventListener('load', handleRouteComplete);
    };
  }, [router]);

  return (
    <>
      {showPreloader && (
        <div className="preloader fixed inset-0 bg-white flex items-center justify-center z-50">
          <div className="preloader__container w-64 h-2 bg-gray-200 rounded-full overflow-hidden">
            <div className="preloader__progress h-full bg-black rounded-full"></div>
          </div>
        </div>
      )}
      <div className="page-content">{children}</div>
    </>
  );
}

优化点

  • 可替换进度条为自定义动画(如SVG路径绘制、形状变形),用GSAP的drawSVG或morphSVG插件增强视觉效果
  • 结合useRouter().isPending状态,更精准控制动画触发时机

二、Barba.js风格容器式页面过渡

核心思路

利用Next.js App Router的template作为过渡容器,通过路由切换前后的内容容器切换,配合GSAP实现旧页面退场、新页面入场的无缝动画,模拟Barba的容器替换逻辑。

代码实现示例

在template.tsx中:

'use client';
import { useState, useEffect, useRef } from 'react';
import { useRouter, usePathname } from 'next/navigation';
import gsap from 'gsap';

export default function TransitionTemplate({ children }: { children: React.ReactNode }) {
  const [isTransitioning, setIsTransitioning] = useState(false);
  const [currentContent, setCurrentContent] = useState(children);
  const router = useRouter();
  const pathname = usePathname();
  const contentRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const handleBeforeNavigate = () => {
      setIsTransitioning(true);
      // 根据目标路由做差异化退场动画
      const exitTl = gsap.timeline();
      if (pathname === '/') {
        exitTl.to(contentRef.current, { opacity: 0, scale: 0.95, duration: 0.4, ease: 'power2.in' });
      } else {
        exitTl.to(contentRef.current, { opacity: 0, x: -50, duration: 0.4, ease: 'power2.in' });
      }
    };

    const handleAfterNavigate = () => {
      // 等待DOM更新后触发新页面入场动画
      setTimeout(() => {
        setCurrentContent(children);
        const enterTl = gsap.timeline();
        if (pathname === '/contact' || pathname === '/about') {
          enterTl.from(contentRef.current, { opacity: 0, x: 50, duration: 0.4, ease: 'power2.out' });
        } else {
          enterTl.from(contentRef.current, { opacity: 0, scale: 1.05, duration: 0.4, ease: 'power2.out' });
        }
        enterTl.eventCallback('onComplete', () => setIsTransitioning(false));
      }, 100);
    };

    router.beforeNavigate(handleBeforeNavigate);
    router.afterNavigate(handleAfterNavigate);

    return () => {
      router.beforeNavigate(null);
      router.afterNavigate(null);
    };
  }, [router, children, pathname]);

  return (
    <div className="transition-container">
      {!isTransitioning && (
        <div ref={contentRef} className="page-content">{currentContent}</div>
      )}
      {/* 全屏遮罩增强沉浸感 */}
      {isTransitioning && (
        <div className="transition-overlay fixed inset-0 bg-black/80 z-40"></div>
      )}
    </div>
  );
}

进阶优化

  • 通过usePathname()区分不同路由,实现针对性的过渡动画(如首页到联系页用横向滑动,到关于页用缩放)
  • 用GSAP的timeline组合多步动画(遮罩入场→内容切换→遮罩退场)
  • 结合layout.tsx实现局部内容过渡,而非全页切换

参考方向

  • GitHub仓库:搜索nextjs app router gsap page transitions,参考作品集类项目的预加载器和过渡实现逻辑
  • GSAP官方文档:重点学习timeline、插件(如morphSVG)的使用,以及路由事件结合动画的最佳实践

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 23:31:18