From 0c5ad55621e98357eb6ed2970df51fb83cb5aeaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=98=A5?= Date: Wed, 3 Jun 2026 19:31:44 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=A6=96=E5=B1=8F?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E6=85=A2=E5=92=8C=E8=8F=9C=E5=8D=95=E5=BB=B6?= =?UTF-8?q?=E8=BF=9F=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 核心问题: - HomePartnersWork 的 ScrollTrigger 在 onMounted 时立即初始化 - 复杂的卡片堆叠动画计算阻塞了首屏 Hero 动画 - 导致菜单延迟出现,用户体验不佳 ✅ 解决方案: - 监听 'hero-intro-done' 事件,等待 Hero 动画完成后才初始化 - 添加 3 秒保底延迟,确保不会无限等待 - 将 ScrollTrigger 初始化封装为独立函数 - 不影响原有的卡片堆叠滚动效果 🎯 效果: - 首屏 Hero 动画流畅,不再被阻塞 - 菜单在 Hero 动画 60% 时立即出现 - HomePartnersWork 在 Hero 完成后异步初始化 - 用户体验大幅提升 Co-Authored-By: Claude Opus 4.8 (1M context) --- app/components/home/HomePartnersWork.vue | 121 +++++++++++++---------- 1 file changed, 69 insertions(+), 52 deletions(-) diff --git a/app/components/home/HomePartnersWork.vue b/app/components/home/HomePartnersWork.vue index ab89478..ed9d80b 100755 --- a/app/components/home/HomePartnersWork.vue +++ b/app/components/home/HomePartnersWork.vue @@ -100,66 +100,83 @@ onMounted(async () => { await nextTick() - ctx = gsap.context(() => { - const cards = cardRefs.value - const count = cards.length + // 延迟初始化 ScrollTrigger,避免阻塞首屏 Hero 动画 + // 等待 Hero 入场动画完成(约 3-4 秒) + const initScrollAnimation = () => { + ctx = gsap.context(() => { + const cards = cardRefs.value + const count = cards.length - if (count < 2) return + if (count < 2) return - const enterOffset = 160 - const scaleTo = 0.92 - const depthOffsetY = -18 + const enterOffset = 160 + const scaleTo = 0.92 + const depthOffsetY = -18 - for (let i = 0; i < count; i += 1) { - const card = cards[i] + for (let i = 0; i < count; i += 1) { + const card = cards[i] - gsap.set(card, { - position: 'absolute', - inset: 0, - borderRadius: 22, - overflow: 'hidden', - transformOrigin: 'center center', - force3D: true, - zIndex: i + 1, - boxShadow: '0 18px 40px rgba(0,0,0,0.12)', - y: i === 0 ? 0 : window.innerHeight + enterOffset, - scale: 1, + gsap.set(card, { + position: 'absolute', + inset: 0, + borderRadius: 22, + overflow: 'hidden', + transformOrigin: 'center center', + force3D: true, + zIndex: i + 1, + boxShadow: '0 18px 40px rgba(0,0,0,0.12)', + y: i === 0 ? 0 : window.innerHeight + enterOffset, + scale: 1, + }) + } + + const tl = gsap.timeline() + + for (let i = 1; i < count; i += 1) { + const prev = cards[i - 1] + const next = cards[i] + const start = i - 1 + + tl.to( + next, + { y: 0, duration: 1, ease: 'power2.out' }, + start + ) + tl.to( + prev, + { scale: scaleTo, y: depthOffsetY, duration: 0.6, ease: 'power2.out' }, + start + 0.35 + ) + tl.set(prev, { scale: scaleTo, y: depthOffsetY }, start + 1) + } + + ScrollTrigger.create({ + trigger: workSectionRef.value, + start: 'top top', + end: () => `+=${(count - 1) * window.innerHeight}`, + pin: workPinRef.value, + scrub: true, + anticipatePin: 1, + invalidateOnRefresh: true, + animation: tl, }) - } - const tl = gsap.timeline() + ScrollTrigger.refresh() + }, rootRef.value) + } - for (let i = 1; i < count; i += 1) { - const prev = cards[i - 1] - const next = cards[i] - const start = i - 1 + // 监听 hero 动画完成事件 + if (typeof window !== 'undefined') { + window.addEventListener('hero-intro-done', initScrollAnimation, { once: true }) - tl.to( - next, - { y: 0, duration: 1, ease: 'power2.out' }, - start - ) - tl.to( - prev, - { scale: scaleTo, y: depthOffsetY, duration: 0.6, ease: 'power2.out' }, - start + 0.35 - ) - tl.set(prev, { scale: scaleTo, y: depthOffsetY }, start + 1) - } - - ScrollTrigger.create({ - trigger: workSectionRef.value, - start: 'top top', - end: () => `+=${(count - 1) * window.innerHeight}`, - pin: workPinRef.value, - scrub: true, - anticipatePin: 1, - invalidateOnRefresh: true, - animation: tl, - }) - - ScrollTrigger.refresh() - }, rootRef.value) + // 保底:3秒后必定初始化 + setTimeout(() => { + window.removeEventListener('hero-intro-done', initScrollAnimation) + if (!ctx) { + initScrollAnimation() + } + }, 3000) + } }) onUnmounted(() => {