fix: 修复所有首页板块 pin 滚动不连贯问题
🐛 问题根源: 每个板块有两套独立的 ScrollTrigger 在同一个元素上: - 入场动画用独立的 scrollTrigger - pin 效果用 ScrollTrigger.create 两者互相冲突,导致空白间隙和动画跳变 ✅ 修复方案: 将所有板块的入场动画和 pin 滚动合并为一个统一时间线(timeline), 确保动画从 pin 开始到结束完全连续,无空白区 🔧 修复的 6 个板块: 1. HomeTestimonials - 标题入场 + 卡片逐张切换(淡入淡出) 2. HomeServices - 标题入场 + 卡片飞入归位 + 逐张聚焦 3. HomeAchievements - 逐个成就项展示(前一个淡出,后一个淡入) 4. HomeTechStack - 内容入场 + 标签散开 + 球体旋转 5. HomeProcess - 标题入场 + 线条绘制 + 步骤逐个出现 + 横向滚动 6. HomeUpdates - 标题入场 + 卡片分批出现 🎯 效果: - pin 期间动画完全连续,无空白间隙 - 入场和 pin 共享同一个 timeline - scrub: 0.5 让滚动更平滑 - 每个板块到下一个板块无缝过渡 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -226,58 +226,34 @@ onMounted(async () => {
|
||||
if (!rootRef.value || !headerRef.value) return
|
||||
|
||||
ctx = gsap.context(() => {
|
||||
// 标题动画
|
||||
gsap.fromTo(headerRef.value,
|
||||
{ autoAlpha: 0, y: 50 },
|
||||
{
|
||||
scrollTrigger: {
|
||||
trigger: headerRef.value,
|
||||
start: 'top 80%',
|
||||
toggleActions: 'play none none reverse'
|
||||
},
|
||||
autoAlpha: 1,
|
||||
y: 0,
|
||||
duration: 1,
|
||||
ease: 'power3.out'
|
||||
}
|
||||
)
|
||||
|
||||
// 卡片从中心扩散入场
|
||||
// 初始状态:标题隐藏,卡片居中缩小
|
||||
gsap.set(headerRef.value, { autoAlpha: 0, y: 50 })
|
||||
cardElements.value.forEach((card, index) => {
|
||||
if (!card) return
|
||||
|
||||
gsap.fromTo(card,
|
||||
{ autoAlpha: 0, scale: 0.5 },
|
||||
{
|
||||
scrollTrigger: {
|
||||
trigger: rootRef.value,
|
||||
start: 'top 70%',
|
||||
toggleActions: 'play none none reverse'
|
||||
},
|
||||
autoAlpha: 1,
|
||||
scale: 1,
|
||||
duration: 0.8,
|
||||
delay: index * 0.1,
|
||||
ease: 'back.out(1.5)',
|
||||
onComplete: () => {
|
||||
if (index === cardElements.value.length - 1) {
|
||||
updateCards()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
gsap.set(card, { autoAlpha: 0, scale: 0.5 })
|
||||
})
|
||||
|
||||
// Pin 滚动效果:固定板块,滚动切换卡片
|
||||
// 统一时间线:入场 + pin 滚动切换
|
||||
const totalCards = testimonials.length
|
||||
const timeline = gsap.timeline()
|
||||
const tl = gsap.timeline()
|
||||
|
||||
// 为每张卡片创建切换动画(每屏切换一张)
|
||||
for (let i = 0; i < totalCards - 1; i++) {
|
||||
timeline.to({}, { duration: 1 }, i)
|
||||
timeline.call(() => {
|
||||
goToSlide(i + 1)
|
||||
}, [], i + 0.5)
|
||||
// 第一屏:标题入场 + 第一张卡片出现
|
||||
tl.to(headerRef.value, { autoAlpha: 1, y: 0, duration: 0.8, ease: 'power3.out' }, 0)
|
||||
tl.to(cardElements.value[0], { autoAlpha: 1, scale: 1, duration: 0.8, ease: 'back.out(1.5)' }, 0.3)
|
||||
tl.call(() => { currentIndex.value = 0 }, [], 0.3)
|
||||
|
||||
// 后续屏:滚动切换卡片,每屏切一张
|
||||
for (let i = 1; i < totalCards; i++) {
|
||||
const pos = i
|
||||
// 当前卡片缩小淡出
|
||||
tl.to(cardElements.value[i - 1], {
|
||||
autoAlpha: 0, scale: 0.5, duration: 0.5, ease: 'power2.in'
|
||||
}, pos)
|
||||
// 新卡片放大淡入
|
||||
tl.to(cardElements.value[i], {
|
||||
autoAlpha: 1, scale: 1, duration: 0.6, ease: 'back.out(1.5)'
|
||||
}, pos + 0.3)
|
||||
tl.call(() => { currentIndex.value = i }, [], pos + 0.3)
|
||||
}
|
||||
|
||||
ScrollTrigger.create({
|
||||
@@ -285,9 +261,9 @@ onMounted(async () => {
|
||||
start: 'top top',
|
||||
end: () => `+=${totalCards * window.innerHeight}`,
|
||||
pin: true,
|
||||
scrub: true,
|
||||
scrub: 0.5,
|
||||
anticipatePin: 1,
|
||||
animation: timeline,
|
||||
animation: tl,
|
||||
})
|
||||
}, rootRef.value)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user