Nuxt3中如何实现超出el元素范围的definePageMeta页面过渡?
解决Nuxt3页面过渡中调用组件方法实现百叶窗效果的问题
核心原因
definePageMeta中的pageTransition钩子(比如onEnter)是在页面组件实例创建之前执行的,无法访问页面内的组件实例,自然也就调用不到你通过defineExpose暴露的方法,这是当前问题的根本原因。
可行方案建议
1. 改用页面内的<Transition>组件(推荐)
放弃definePageMeta的全局页面过渡配置,在页面组件内部用Vue原生的<Transition>包裹页面内容,这样过渡钩子处于页面组件上下文内,能直接调用组件方法。
示例代码:
<template> <Transition name="blind-transition" @enter="handleEnter"> <div class="page-content"> <!-- 页面内容 --> <BlindComponent ref="blindRef" /> </div> </Transition> </template> <script setup> import gsap from 'gsap' const blindRef = ref(null) const handleEnter = (el, done) => { // 调用组件暴露的方法触发百叶窗展开 blindRef.value.startBlindAnimation() // 配合GSAP控制整体过渡,完成后调用done gsap.timeline({ onComplete: done }).to(el, { autoAlpha: 1, duration: 0.5 }) } </script> <style> /* 可选:添加基础过渡样式 */ .blind-transition-enter-from { opacity: 0; } </style>
这种方式完全贴合Vue的组件生命周期,能轻松实现页面组件和过渡逻辑的交互,是最适合你场景的方案。
2. 全局过渡层+路由事件控制
如果希望保留全局过渡的统一性,可以在app.vue中添加一个全局的百叶窗过渡层,通过Nuxt的路由事件(如page:start、page:finish)来触发动画,无需依赖页面组件的方法。
示例代码(app.vue):
<template> <div> <NuxtPage /> <!-- 全局百叶窗过渡层 --> <div class="blind-overlay" ref="blindOverlay"> <div class="blind-item" v-for="i in 8" :key="i"></div> </div> </div> </template> <script setup> import gsap from 'gsap' const blindOverlay = ref(null) const router = useRouter() // 监听路由开始切换 router.beforeEach((to, from, next) => { // 触发百叶窗关闭动画 gsap.to('.blind-item', { height: '100vh', duration: 0.3, stagger: 0.05, onComplete: next }) }) // 监听路由切换完成 router.afterEach(() => { // 触发百叶窗打开动画 gsap.to('.blind-item', { height: 0, duration: 0.3, stagger: 0.05 }) }) </script> <style> .blind-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100vh; display: flex; z-index: 9999; } .blind-item { flex: 1; background: #000; height: 100vh; } </style>
这种方案适合所有页面共用相同百叶窗过渡的场景,无需在每个页面单独配置。
3. 自定义全局过渡组件
创建一个Vue组件作为全局页面过渡效果,注册后在definePageMeta中指定过渡名称,组件内部自行管理百叶窗动画,无需依赖页面组件。
步骤:
- 创建
components/Transition/BlindPageTransition.vue:
<template> <Transition> <slot /> <div class="blind-container" ref="blindContainer"> <div class="blind-slice" v-for="i in 10" :key="i"></div> </div> </Transition> </template> <script setup> import gsap from 'gsap' const blindContainer = ref(null) const enter = (el, done) => { // 初始化百叶窗状态 gsap.set('.blind-slice', { height: '100vh' }) // 执行展开动画 gsap.timeline({ onComplete: done }).to('.blind-slice', { height: 0, duration: 0.4, stagger: 0.03 }) } defineExpose({ enter }) </script> <style scoped> .blind-container { position: fixed; top: 0; left: 0; width: 100%; height: 100vh; display: flex; z-index: 100; } .blind-slice { flex: 1; background-color: #222; } </style>
- 在
nuxt.config.ts中注册全局过渡组件:
export default defineNuxtConfig({ app: { pageTransition: { name: 'blind-page', component: 'BlindPageTransition' } } })
这种方案既保留了definePageMeta的全局配置能力,又能实现自定义的百叶窗效果,适合需要统一管理过渡样式的场景。
关于onMounted的疑问
不推荐用onMounted钩子来实现页面过渡,因为onMounted是页面挂载完成后才触发,无法和路由切换的时机精准同步,容易出现页面先显示再执行动画的突兀效果,破坏过渡的流畅性。
内容的提问来源于stack exchange,提问作者Thomas James Thorstensson
相关产品推荐
相关产品推荐

