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

如何为React Router v4(搭配react-router-config)配置JS非CSS动画(含GSAP)

嘿,刚好我之前折腾过React Router v4 + react-router-config + GSAP的JS动画方案,踩过几个小坑,给你梳理下具体的实现步骤,应该能帮到你:

1. 先搭好react-router-config的基础路由框架

首先得确保你的路由配置是基于react-router-config的数组格式,这是后续做动画的基础:

// routes.js
import Home from './pages/Home';
import About from './pages/About';
import Contact from './pages/Contact';

export default [
  {
    path: '/',
    exact: true,
    component: Home,
    // 可以给需要特殊动画的路由加meta字段
    meta: { animation: 'fade' }
  },
  {
    path: '/about',
    component: About,
    meta: { animation: 'slideLeft' }
  },
  {
    path: '/contact',
    component: Contact,
    meta: { animation: 'slideRight' }
  }
];

然后在根组件里用RenderRoutes渲染路由:

// App.js
import { RenderRoutes } from 'react-router-config';
import routes from './routes';

function App() {
  return <RenderRoutes routes={routes} />;
}
2. 封装全局过渡组件,结合GSAP做动画

核心思路是监听路由location的变化,在路由切换前后触发GSAP动画,同时控制动画期间的路由拦截(避免用户快速点跳转搞乱动画)。

这里用函数组件+hooks来实现,类组件逻辑类似:

// components/PageTransition.js
import React, { useState, useEffect, useRef } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { gsap } from 'gsap';

const PageTransition = ({ children }) => {
  const location = useLocation();
  const navigate = useNavigate();
  const [isAnimating, setIsAnimating] = useState(false);
  const contentRef = useRef(null);
  const prevLocation = useRef(location);

  // 监听路由变化,触发动画
  useEffect(() => {
    if (location.pathname !== prevLocation.current.pathname) {
      setIsAnimating(true);
      
      // 先执行当前页面的退出动画
      const exitAnim = gsap.timeline({
        onComplete: () => {
          // 退出动画完成后,更新路由记录,执行新页面的进入动画
          prevLocation.current = location;
          const enterAnim = gsap.timeline();
          
          // 根据路由meta选择对应的进入动画
          const animationType = location.state?.animation || findRouteMeta(location.pathname)?.animation || 'fade';
          switch(animationType) {
            case 'slideLeft':
              enterAnim.fromTo(contentRef.current, { x: '100%', opacity: 0 }, { x: 0, opacity: 1, duration: 0.6 });
              break;
            case 'slideRight':
              enterAnim.fromTo(contentRef.current, { x: '-100%', opacity: 0 }, { x: 0, opacity: 1, duration: 0.6 });
              break;
            default:
              enterAnim.fromTo(contentRef.current, { opacity: 0 }, { opacity: 1, duration: 0.4 });
          }
          
          enterAnim.eventCallback('onComplete', () => setIsAnimating(false));
        }
      });

      // 退出动画逻辑(同样可以根据meta定制)
      exitAnim.to(contentRef.current, { opacity: 0, duration: 0.3 });
    }
  }, [location]);

  // 动画期间拦截路由跳转,避免冲突
  useEffect(() => {
    const unblock = navigate.block((tx) => {
      if (isAnimating) {
        alert('动画正在进行中,请稍后再操作'); // 也可以静默阻止,这里给用户提示
        return false;
      }
      unblock();
      tx.retry();
    });

    return unblock;
  }, [isAnimating, navigate]);

  // 辅助函数:根据路径找到对应的路由meta
  const findRouteMeta = (path) => {
    const matchedRoute = routes.find(route => route.path === path);
    return matchedRoute?.meta;
  };

  return (
    <div 
      ref={contentRef} 
      style={{ 
        position: 'fixed', 
        top: 0, 
        left: 0, 
        width: '100%', 
        minHeight: '100vh',
        overflow: 'hidden'
      }}
    >
      {/* 动画期间不渲染新内容,避免DOM混乱 */}
      {!isAnimating && children}
    </div>
  );
};

export default PageTransition;
3. 把过渡组件整合到路由渲染中

修改根组件,用PageTransition包裹RenderRoutes:

// App.js
import { RenderRoutes } from 'react-router-config';
import PageTransition from './components/PageTransition';
import routes from './routes';

function App() {
  return (
    <PageTransition>
      <RenderRoutes routes={routes} />
    </PageTransition>
  );
}
4. 特殊场景的处理技巧
  • 嵌套路由动画:如果有嵌套路由,把过渡组件放在需要动画的层级,比如只给一级路由加过渡,嵌套路由内部的动画单独处理。
  • 动态路由参数变化:比如/user/:id切换时,location.pathname会变化,如果你不想触发页面级动画,可以监听location.search或location.state来区分。
  • GSAP实例清理:如果用了ScrollTrigger等高级插件,记得在路由切换时销毁实例,避免内存泄漏:
    import { ScrollTrigger } from 'gsap/ScrollTrigger';
    
    // 在退出动画开始前清理
    ScrollTrigger.getAll().forEach(trigger => trigger.kill());
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 06:18:02