fix(动画): 修复首页动画重复播放和顶部导航显示问题

修复首页动画在特定条件下可能重复播放的问题,通过添加 hasPlayedIntro 状态控制
优化顶部导航的显示逻辑,增加即时显示和隐藏功能,并清理事件监听和定时器
This commit is contained in:
2026-04-27 17:01:39 +08:00
parent 500bdda0e5
commit 52112284cb
2 changed files with 70 additions and 30 deletions
+55 -22
View File
@@ -68,6 +68,7 @@ const playHeaderIntro = () => {
duration: 1.4,
ease: 'power4.out',
delay: 0,
overwrite: 'auto',
clearProps: 'transform',
onComplete: () => {
// 动画完成时,强制把 style 的 opacity 移除,防止被内联样式锁死
@@ -76,6 +77,7 @@ const playHeaderIntro = () => {
}
// 进场动画完毕后,再将滚动消失动画挂载到 ScrollTrigger
if (topHeaderTween) {
topHeaderTween.scrollTrigger?.kill()
ScrollTrigger.create({
animation: topHeaderTween,
start: 0,
@@ -93,6 +95,39 @@ const handleHeroReady = () => {
playHeaderIntro()
}
const cleanupHeroReadyHook = () => {
if (fallbackTimer) {
clearTimeout(fallbackTimer)
fallbackTimer = null
}
if (typeof window !== 'undefined') {
window.removeEventListener('hero-intro-done', handleHeroReady)
}
}
const hideTopHeader = () => {
if (!topHeaderRef.value) return
gsap.set(topHeaderRef.value, { y: '100vh', opacity: 0 })
}
const showTopHeaderInstant = () => {
if (!topHeaderRef.value) return
gsap.set(topHeaderRef.value, { y: 0, opacity: 1 })
topHeaderRef.value.style.opacity = ''
if (topHeaderTween) {
topHeaderTween.scrollTrigger?.kill()
ScrollTrigger.create({
animation: topHeaderTween,
start: 0,
end: 200,
scrub: true,
})
ScrollTrigger.refresh()
}
}
onMounted(() => {
// 顶部导航在页面刚开始滚动的前 200px 内逐渐上移并隐藏。
topHeaderTween = gsap.to(topHeaderRef.value, {
@@ -130,36 +165,34 @@ onMounted(() => {
})
ScrollTrigger.refresh()
if (!appStore.isAppLoading && route.path !== '/') {
showTopHeaderInstant()
}
})
// 监听全局加载状态,当加载完成后再决定 Header 的进场时机
watch(() => appStore.isAppLoading, (isLoading) => {
if (!isLoading && typeof window !== 'undefined') {
if (route.path === '/') {
// 首页:等待自定义事件
window.addEventListener('hero-intro-done', handleHeroReady, { once: true })
watch([() => appStore.isAppLoading, () => route.path], ([isLoading, path]) => {
if (isLoading || typeof window === 'undefined') return
// 增加一个保底的 setTimeout,防止首页的动画事件因为某种原因未能触发
// 从 loading 结束开始算起,首页的 intro 和 hero 动画加起来大约 3~4 秒
fallbackTimer = setTimeout(() => {
window.removeEventListener('hero-intro-done', handleHeroReady)
playHeaderIntro()
}, 5000)
} else {
// 非首页:加载完成后直接进场
cleanupHeroReadyHook()
if (path === '/') {
hideTopHeader()
window.addEventListener('hero-intro-done', handleHeroReady, { once: true })
fallbackTimer = setTimeout(() => {
window.removeEventListener('hero-intro-done', handleHeroReady)
playHeaderIntro()
}
}, 5000)
return
}
showTopHeaderInstant()
}, { immediate: true })
onBeforeUnmount(() => {
if (fallbackTimer) {
clearTimeout(fallbackTimer)
fallbackTimer = null
}
if (typeof window !== 'undefined') {
window.removeEventListener('hero-intro-done', handleHeroReady)
}
cleanupHeroReadyHook()
topHeaderTween?.scrollTrigger?.kill()
topHeaderTween?.kill()
bottomHeaderScrollTrigger?.kill()