TanStack Router 实战:基于 View Transitions API 实现丝滑的页面转场动画
发布时间:2026/9/15 21:54:00来源:尧图网络
TanStack Router 实战基于 View Transitions API 实现丝滑的页面转场动画【免费下载链接】router A client-first, server-capable, fully type-safe router and full-stack framework for the web (React and more).项目地址: https://gitcode.com/GitHub_Trending/ro/router导读本文以仓库中的 view-transitions 示例 为主线深入讲解如何在 TanStack RouterReact中集成浏览器原生 View Transitions API实现平滑的页面过渡与动画化路由切换。读完本文你将掌握Link组件上的viewTransition属性、createRouter的defaultViewTransition全局配置、基于active-view-transition-type的 CSS 动画编写以及如何根据导航方向动态决定转场类型——这些能力可直接复用到你自己的项目里为 SPA 增添接近原生应用的交互质感。示例概览一个展示多种转场效果的完整应用examples/react/view-transitions是一个专门演示 View Transitions 集成方式的 React 示例应用。它不像一般示例那样只做淡入淡出而是构造了多组风格迥异的转场并在真实路由跳转中演示每种写法的落地形态View Transitions API 集成将浏览器原生的视图过渡能力接入路由导航流程平滑页面转场旧页面与新页面在切换时执行可控的过渡动画动画化路由变化不同路由跳转触发不同类型的动画滑动、旋转模糊“warp”等增强用户体验在保持 SPA 无刷新导航优势的同时让界面切换更有连续性和质感。该示例的应用结构非常典型包含根路由、首页、文章列表与文章详情等路由目录布局如下examples/react/view-transitions/ ├── index.html # HTML 入口 ├── package.json # 依赖与脚本 ├── vite.config.js # Vite TanStack Router 插件配置 ├── tsconfig.json └── src/ ├── main.tsx # createRouter 实例与渲染入口 ├── posts.tsx # 文章数据获取loader 使用 ├── routeTree.gen.ts # 由插件自动生成的路由树 ├── styles.css # 全部转场动画的 CSS 定义 └── routes/ ├── __root.tsx # 根路由导航栏 Outlet ├── index.tsx # 首页进入时 slide-left ├── how-it-works.tsx # 转场说明页双向滑动 ├── explore.tsx # CSS 探索页返回时 slide-right ├── posts.route.tsx # 文章列表布局warp 动态转场 ├── posts.index.tsx # 列表默认页 └── posts.$postId.tsx # 文章详情加载器 错误/未找到组件快速开始三条命令跑通示例原文档提供了从零启动该示例的完整流程直接照做即可。如果你想基于此示例创建一个全新项目使用gitpick拉取模板npx gitpick TanStack/router/tree/main/examples/react/view-transitions view-transitions进入项目后安装依赖并启动开发服务器pnpm install pnpm dev开发服务器默认运行在http://localhost:3000由 package.json 中的dev: vite --port 3000指定。生产构建与类型检查pnpm buildbuild脚本为vite build tsc --noEmit即先产出产物、再做全量类型检查保证转场相关代码的类型安全在发布前即被验证。一、转场的开关viewTransition属性的三种用法在 TanStack Router 中触发 View Transitions 的最小粒度是导航组件本身。示例中所有Link都通过viewTransition属性开启转场它有三种形态覆盖从“一键开启”到“完全自定义”的诉求。1. 布尔值简单开启默认转场在根路由导航栏 __root.tsx 中Home 与 Posts 链接直接使用viewTransition布尔属性浏览器将采用默认的 View Transitions 行为完成页面切换Link to/ activeProps{{ className: font-bold }} activeOptions{{ exact: true }} viewTransition Home /Link Link to/posts activeProps{{ className: font-bold }} viewTransition Posts /Link这也是最省事的接入方式只要浏览器支持 View Transitions API路由切换即自动附带原生过渡效果无需编写任何自定义 CSS。2. 类型数组指定命名的转场类型当你想让某次导航使用特定的动画时传入{ types: [...] }。首页跳转到“工作原理”页时index.tsxLink to{/how-it-works} // 见 styles.css 中的 slide-left 转场 viewTransition{{ types: [slide-left] }} classNamefont-bold Next Page -gt; /Link这里的types数组会被写入过渡的active-view-transition-type中供 CSS 选择器匹配详见下文 CSS 章节。how-it-works页 how-it-works.tsx 则同时给出了两个方向的链接返回首页用[slide-right]前进到探索页用[slide-left]——用类型区分方向语义清晰。3. 类型函数根据导航上下文动态决策这是最强大也最复杂的形态。types可以是一个函数它接收{ fromLocation, toLocation }分别为出发地与目的地的 location 信息返回类型数组或falsefalse表示本次导航不做转场。文章列表 posts.route.tsx 用它实现了“向前翻文章用 warp 动画、向后翻用 warp-backwards 动画”的方向感知效果viewTransition{{ types: ({ fromLocation, toLocation }) { const fromRoute router .matchRoutes(fromLocation?.pathname ?? /) .find((entry) entry.routeId /posts/$postId) const toRoute router .matchRoutes(toLocation?.pathname ?? /) .find((entry) entry.routeId /posts/$postId) const fromIndex Number(fromRoute?.params.postId) const toIndex Number(toRoute?.params.postId) if ( Number.isNaN(fromIndex) || Number.isNaN(toIndex) || fromIndex toIndex ) { return false // 不做转场 } return fromIndex toIndex ? [warp-backwards] : [warp] }, }}这段代码值得拆解通过router.matchRoutes(pathname)解析出目标路径对应的路由匹配项再定位到/posts/$postId这条路由从而取得新旧两篇文章的postId参数将postId数值化后比较大小fromIndex toIndex说明在往回翻返回[warp-backwards]否则返回[warp]当参数缺失、无法解析或两篇文章相同同一篇的刷新式导航时返回false明确关闭转场避免无意义的动画。注意fromLocation可能为undefined例如首次直接着陆到某篇文章因此代码用?? /兜底这是函数式写法里值得留意的健壮性细节。二、全局配置defaultViewTransition一劳永逸示例在 main.tsx 中创建 Router 实例时注释里完整展示了defaultViewTransition的两种全局形态const router createRouter({ routeTree, defaultPreload: intent, defaultStaleTime: 5000, scrollRestoration: true, /* 使用 defaultViewTransition 可以免去在每个导航上手动添加 viewTransition: true 的重复劳动。 如果 defaultViewTransition.types 是一个函数它会被调用并接收 location 变化信息应返回一个 view transition 类型数组。 这在你想根据不同导航的具体情况应用不同转场时非常有用。 一个典型场景根据浏览器历史前进/后退时前后路由的索引 决定滑动方向。 */ // defaultViewTransition: true // OR // defaultViewTransition: { // types: ({ fromLocation, toLocation }) { // let direction none // if (fromLocation) { // const fromIndex fromLocation.state.__TSR_index // const toIndex toLocation.state.__TSR_index // direction fromIndex toIndex ? right : left // } // return [slide-${direction}] // }, // }, })要点归纳defaultViewTransition: true全局开启默认转场所有导航自动拥有过渡效果个别需要关闭的导航可再通过局部的viewTransition{false}覆盖defaultViewTransition: { types: fn }全局提供一个统一的类型决策函数接收{ fromLocation, toLocation }返回类型数组。示例注释给出的是方向感知场景——读取location.state.__TSR_indexTanStack Router 注入的历史索引状态根据前进/后退方向返回slide-left或slide-right优先级局部Link上的viewTransition会覆盖全局默认值两者可以组合使用全局兜底、局部定制。从源码结构看defaultViewTransition与Link.viewTransition走的是同一套类型系统ViewTransitionOptions函数式types的入参结构也保持一致因此上面 posts 列表中的方向判断逻辑完全可以上提到全局配置中实现全站统一的方向感知转场。除了转场这段配置还展示了示例使用的其他 Router 级优化defaultPreload: intent悬停/聚焦时预加载目标路由资源、defaultStaleTime: 5000loader 数据 5 秒内不重复请求、scrollRestoration: true滚动位置恢复这些与转场一起构成了流畅导航的完整体验。三、动画的归宿active-view-transition-type与 CSS 关键帧类型数组只是“信号”真正决定动画长什么样的是 styles.css 中的 CSS 规则。该文件通过:active-view-transition-type(...)伪类选择器匹配导航时激活的转场类型再配合::view-transition-old()/::view-transition-new()伪元素分别控制旧视图的退出动画与新视图的进入动画。1. 左右滑动slide-left/slide-righthtml:active-view-transition-type(slide-left) { ::view-transition-old(main-content) { animation: 300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-out-left; } ::view-transition-new(main-content) { animation: 300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-in-left; } }配套的关键帧keyframes slide-out-left { from { transform: translateX(0); } to { transform: translateX(-100%); } } keyframes slide-in-left { from { transform: translateX(100%); } to { transform: translateX(0); } }slide-right与之镜像对称styles.css。注意这里使用了嵌套 CSS 语法::view-transition-*这是现代 CSS 原生嵌套写法配合 Tailwind CSS v4 编译即可使用。动画时长 300ms、缓动cubic-bezier(0.4, 0, 0.2, 1)Material 风格的进出缓动、both填充模式保证动画前后状态稳定。2. 视口内元素的独立命名view-transition-name滑动动画的作用目标是main-content与post这两个具名视图。示例通过 Tailwind 的任意属性语法给元素命名例如首页与各页面容器index.tsxdiv classNamep-2 [view-transition-name:main-content]以及文章详情区域的独立视图posts.route.tsxdiv className[view-transition-name:post] Outlet / /div命名视图named view允许你把转场精确作用到某个具体 DOM 子树而不是整个文档快照。这在本示例中的意义是main-content负责页面主体内容的滑动post负责文章详情区域的 warp 动画二者互不干扰、可并行播放这正是 View Transitions API 相比整页 fade 的核心优势。3. 旋转模糊特效warp/warp-backwards文章详情切换使用了更夸张的“扭曲”效果styles.csshtml:active-view-transition-type(warp) { ::view-transition-old(post) { animation: 400ms ease-out both warp-out; } ::view-transition-new(post) { animation: 400ms ease-out both warp-in; } }warp-out从清晰缩放状态过渡到“模糊 提亮 放大 旋转 90°”的消散态keyframes warp-out { from { opacity: 1; filter: blur(0) brightness(1); transform: scale(1) rotate(0deg); } to { opacity: 0; filter: blur(15px) brightness(1.8); transform: scale(1.1) rotate(90deg); } }warp-in则从模糊的缩小旋转态恢复为清晰态warp-in-backwards/warp-out-backwards将旋转方向取反styles.css从而实现“前进 warp、后退 warp-backwards”的方向差异。4. 浏览器兼容性提示示例在探索页 explore.tsx 明确给出免责声明View Transition Types may not be supported in all browsers and will fall back to the default browser transition if not available.即active-view-transition-type与具名类型转场在部分浏览器中不受支持此时 TanStack Router 与浏览器会回退到默认转场行为不会产生异常或白屏。生产项目接入前应结合目标浏览器的支持情况做特性检测或渐进增强。四、数据加载与转场的协作loader 驱动的示例数据为了让转场有“内容”可切换示例通过 posts.tsx 提供文章数据并在路由层使用 TanStack Router 的 loader 机制拉取列表布局路由 posts.route.tsx 通过loader: fetchPosts一次性加载全部文章详情路由 posts.$postId.tsx 通过loader: async ({ params: { postId } }) fetchPost(postId)按参数加载单篇文章并配置了errorComponent与notFoundComponent列表页还特意追加了一个id: i-do-not-exist的假条目posts.route.tsx用来演示点击后触发notFoundComponentPost not found的兜底体验。值得注意的是View Transitions 与数据加载是正交的两条链路。loader 保证新页面在渲染前数据就绪转场动画负责新旧视图切换时的视觉连续性两者组合才能呈现出“数据已加载、画面丝滑切换”的体验这正是 TanStack Router 把预加载defaultPreload与转场同时内置在导航流程中的原因。五、工程配置Vite 插件与类型安全示例的 vite.config.js 展示了路由插件的接入方式import { defineConfig } from vite import react from vitejs/plugin-react import { tanstackRouter } from tanstack/router-plugin/vite import tailwindcss from tailwindcss/vite export default defineConfig({ plugins: [ tailwindcss(), tanstackRouter({ target: react, autoCodeSplitting: true, }), react(), ], })其中tanstackRouter插件负责扫描src/routes生成类型安全的routeTree.gen.ts并开启按路由的自动代码分割autoCodeSplitting: truemain.tsx中的declare module tanstack/react-router { interface Register { router: typeof router } }main.tsx将 Router 实例注册到模块类型中让Link、useRouter等 API 获得完整的路径与参数类型推导——这也是viewTransition的types函数入参fromLocation/toLocation具有精确类型的原因。依赖方面package.json基于tanstack/react-router、React 19、Tailwind CSS v4 与 Vite 构建。六、从示例到实战接入清单与模式总结基于上述分析把 View Transitions 接入你自己的 TanStack Router 项目只需四步启用浏览器能力确认目标浏览器支持 View Transitions API现代 Chromium / Safari 均已支持Firefox 需关注进展开启转场在createRouter中设置defaultViewTransition: true全局开启或逐个在Link上添加viewTransition编写动画 CSS为命名视图如main-content、post声明view-transition-name再用html:active-view-transition-type(...)选择器配合keyframes定义退出/进入动画按需动态决策当不同导航需要不同动画时使用types: ({ fromLocation, toLocation }) [...]函数返回类型数组或false关闭转场。三种types取值形态的对比形态写法适用场景布尔值viewTransition/defaultViewTransition: true全局或局部简单开启默认转场类型数组viewTransition{{ types: [slide-left] }}固定命名的动画类型类型函数viewTransition{{ types: ({ fromLocation, toLocation }) [...] }}按导航方向、路由参数等动态决定返回false可跳过整体而言本示例的价值在于把 View Transitions API 与 TanStack Router 的导航模型做了深度绑定转场不再是脱离路由体系的“外挂脚本”而是Link、createRouter与路由树的一等公民配置。配合预加载、滚动恢复与类型安全你可以在不牺牲 SPA 性能的前提下为应用注入接近原生 App 的转场质感。【免费下载链接】router A client-first, server-capable, fully type-safe router and full-stack framework for the web (React and more).项目地址: https://gitcode.com/GitHub_Trending/ro/router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网