fix(动画): 修复首页动画重复播放和顶部导航显示问题
修复首页动画在特定条件下可能重复播放的问题,通过添加 hasPlayedIntro 状态控制 优化顶部导航的显示逻辑,增加即时显示和隐藏功能,并清理事件监听和定时器
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user