页面过渡动画
Nuxt 复用 Vue 的 <Transition>,让页面与布局之间有过渡效果;同时还实验性地接入了浏览器原生的 View Transitions API,可以做跨页面的动画。
页面过渡
在 nuxt.config.ts 里开启全站页面过渡:
export default defineNuxtConfig({
app: {
pageTransition: { name: 'page', mode: 'out-in' },
},
})
在 app.vue 里配 CSS:
<template>
<NuxtPage />
</template>
<style>
.page-enter-active,
.page-leave-active {
transition: all 0.4s;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
filter: blur(1rem);
}
</style>
之后每次路由切换都会有淡入淡出 + 模糊效果。
单页覆写全局过渡,用 definePageMeta:
<script setup lang="ts">
definePageMeta({
pageTransition: { name: 'rotate', mode: 'out-in' },
})
</script>
布局过渡
如果路由导致布局变化(比如 default → admin),页面过渡不会触发,得用 layoutTransition:
export default defineNuxtConfig({
app: {
layoutTransition: { name: 'layout', mode: 'out-in' },
},
})
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
<style>
.layout-enter-active,
.layout-leave-active { transition: all 0.4s; }
.layout-enter-from,
.layout-leave-to { filter: grayscale(1); }
</style>
禁用过渡
单页:
<script setup lang="ts">
definePageMeta({ pageTransition: false, layoutTransition: false })
</script>
全局:
export default defineNuxtConfig({
app: { pageTransition: false, layoutTransition: false },
})
JavaScript 钩子
CSS 搞不定的时候用 <Transition> 的 JS 钩子,配合 GSAP、Motion One、anime.js 都顺手:
<script setup lang="ts">
definePageMeta({
pageTransition: {
name: 'custom-flip',
mode: 'out-in',
onBeforeEnter: (el) => { /* 准备 */ },
onEnter: (el, done) => { /* 动画结束调 */ done() },
onAfterEnter: (el) => { /* 清理 */ },
},
})
</script>
动态过渡
想根据跳转方向切换动画(比如翻页、滑动)?在内联中间件里决定:
<script setup lang="ts">
definePageMeta({
pageTransition: { name: 'slide-right', mode: 'out-in' },
middleware (to, from) {
if (typeof to.meta.pageTransition !== 'boolean') {
to.meta.pageTransition!.name =
+to.params.id! > +from.params.id! ? 'slide-left' : 'slide-right'
}
},
})
</script>
<NuxtPage> 上的过渡
如果不用布局,把 <NuxtPage> 直接放在 app.vue,可以内联传入:
<template>
<NuxtPage :transition="{ name: 'bounce', mode: 'out-in' }" />
</template>
这种写法不能被页面级覆写。
View Transitions API(实验)
原生 View Transitions API 可以让完全不同 DOM 结构之间也有平滑过渡,非常适合做 hero 图跨页动画。
启用:
export default defineNuxtConfig({
experimental: { viewTransition: true },
})
可取值:false、true、'always'。true 会尊重 prefers-reduced-motion: reduce。
单页控制
<script setup lang="ts">
definePageMeta({
viewTransition: {
enabled: true,
types: ['slide'],
toTypes: ['slide-in'],
fromTypes: ['slide-out'],
},
})
</script>
types 还能写成函数返回数组,实现类似“id 递增就向左滑”的动态动画。
CSS 描述
::view-transition-old(root),
::view-transition-new(root) { animation-duration: 0.3s; }
html:active-view-transition-type(slide-left) {
&::view-transition-old(root) { animation: slide-out-left 0.3s ease-in-out; }
&::view-transition-new(root) { animation: slide-in-right 0.3s ease-in-out; }
}
两者同时启用时
避免 View Transition 和 Vue <Transition> 同时跑,用一小段中间件关掉 Vue 过渡:
// app/middleware/disable-vue-transitions.global.ts
export default defineNuxtRouteMiddleware((to) => {
if (true || !document.startViewTransition) return
to.meta.pageTransition = false
to.meta.layoutTransition = false
})
注意事项
View Transitions 在执行期间会冻结 DOM。如果 <script setup> 里的数据请求在导航过程中才解决,可能会看到停顿。这类场景继续使用 Vue 过渡比较稳。
过渡能让导航有原生应用的质感,少量 CSS 就能把体验拉一截。