解决两个ScrollTrigger冲突导致堆叠效果触发器消失的问题
解决两个ScrollTrigger冲突导致堆叠效果触发器消失的问题
我仔细看了你的代码和问题描述,核心问题出在你在第一个ScrollTrigger的onLeave回调里重复创建第二个ScrollTrigger,再加上两个ScrollTrigger之间的布局冲突,才导致#facts元素莫名消失。咱们一步步来解决:
问题根源拆解
- 重复创建ScrollTrigger:每次触发
onLeave都会新建一个针对#facts的ScrollTrigger,多出来的实例会互相干扰,导致元素布局混乱。 - 布局状态冲突:第一个ScrollTrigger的
pin: "#scroll-container"在onLeave阶段刚释放pin操作,此时页面布局还没稳定,立刻初始化第二个ScrollTrigger会拿到错误的元素尺寸(比如cardHeight可能不准)。 - 动画时序没对齐:你想让堆叠效果在水平滚动完全结束后再运行,但
onLeave的触发时机和第二个ScrollTrigger的start条件没有绑定,导致两者的时序脱节。
修复方案&优化代码
我们把第二个ScrollTrigger的初始化和第一个解耦,通过第一个ScrollTrigger的完成状态来控制第二个的激活,同时避免重复创建实例:
// 先定义全局变量存储两个ScrollTrigger实例,方便后续控制 let horizontalScrollTrigger; let stackingScrollTrigger; let stackingAnimation; function createScrollTriggerMD() { // 初始化水平滚动的ScrollTrigger horizontalScrollTrigger = ScrollTrigger.create({ trigger: "#scroll-container", start: "top top", end: () => `+=${getScrollAmount() * -1}`, pin: "#scroll-container", animation: tween, scrub: 1, onUpdate: (e) => { let scale; let y; if (e.progress <= 0.5) { scale = gsap.utils.mapRange(0, 0.5, 1, 0.6, e.progress); y = gsap.utils.mapRange(0, 0.5, 0, -150, e.progress); } else { scale = gsap.utils.mapRange(0.5, 1, 0.6, 1, e.progress); y = gsap.utils.mapRange(0.5, 1, -150, -150, e.progress); } gsap.to("#red-bubble", { y, scale, x: `-${e.progress * 40}%`, overwrite: "auto", duration: 0.1, }); }, // 用onEnterBack处理回滚时的状态重置 onEnterBack: () => { if (stackingScrollTrigger) { // 回滚时重置堆叠动画并禁用第二个ScrollTrigger stackingAnimation.pause(0); stackingScrollTrigger.disable(); } }, onLeave: () => { // 水平滚动完成后,启用并刷新第二个ScrollTrigger if (stackingScrollTrigger) { stackingScrollTrigger.enable(); stackingScrollTrigger.refresh(); } }, invalidateOnRefresh: true, }); // 提前初始化堆叠动画和第二个ScrollTrigger(但先禁用) initStackingScrollTrigger(); } function initStackingScrollTrigger() { const cards = document.querySelectorAll(".protection-card"); const header = document.querySelector("#facts"); let cardHeight; // 初始化堆叠动画的timeline stackingAnimation = gsap.timeline({ paused: true }); // 封装卡片初始化逻辑,避免重复代码 function initCards() { stackingAnimation.clear(); cardHeight = cards[0].offsetHeight; console.log("initCards()", cardHeight); cards.forEach((card, index) => { if (index > 0) { gsap.set(card, { y: index * cardHeight }); stackingAnimation.to( card, { y: index * 10, duration: index * 0.5, ease: "none" }, 0 ); } }); } // 先调用一次初始化卡片状态 initCards(); // 创建第二个ScrollTrigger,但默认禁用 stackingScrollTrigger = ScrollTrigger.create({ trigger: "#facts", start: "top top", pin: true, end: () => `+=${cards.length * cardHeight + header.offsetHeight}`, scrub: true, animation: stackingAnimation, markers: true, invalidateOnRefresh: true, // 默认禁用,等水平滚动完成再启用 disabled: true, }); // 监听refresh事件,确保窗口 resize 时重新计算卡片尺寸 ScrollTrigger.addEventListener("refreshInit", initCards); }
关键优化点说明
- 提前初始化第二个ScrollTrigger:不再在
onLeave里重复创建,而是提前初始化并设置为disabled: true,等水平滚动完成后再启用,避免实例冲突。 - 回滚状态重置:添加
onEnterBack回调,当用户回滚时重置堆叠动画并禁用第二个ScrollTrigger,保证来回滚动的状态一致性。 - 布局稳定后再刷新:在
onLeave里调用stackingScrollTrigger.refresh(),确保水平滚动的pin释放后,第二个ScrollTrigger能拿到正确的元素尺寸和布局位置,解决#facts消失的问题。 - 代码结构解耦:把两个ScrollTrigger的初始化逻辑分开,后续维护和调试更方便。
这样修改后,堆叠效果会严格在水平滚动完全结束后才触发,#facts元素也不会再莫名消失啦。
内容来源于stack exchange
相关产品推荐
相关产品推荐

