You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决两个ScrollTrigger冲突导致堆叠效果触发器消失的问题

解决两个ScrollTrigger冲突导致堆叠效果触发器消失的问题

我仔细看了你的代码和问题描述,核心问题出在你在第一个ScrollTrigger的onLeave回调里重复创建第二个ScrollTrigger,再加上两个ScrollTrigger之间的布局冲突,才导致#facts元素莫名消失。咱们一步步来解决:

问题根源拆解

  1. 重复创建ScrollTrigger:每次触发onLeave都会新建一个针对#facts的ScrollTrigger,多出来的实例会互相干扰,导致元素布局混乱。
  2. 布局状态冲突:第一个ScrollTrigger的pin: "#scroll-container"在onLeave阶段刚释放pin操作,此时页面布局还没稳定,立刻初始化第二个ScrollTrigger会拿到错误的元素尺寸(比如cardHeight可能不准)。
  3. 动画时序没对齐:你想让堆叠效果在水平滚动完全结束后再运行,但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);
}

关键优化点说明

  1. 提前初始化第二个ScrollTrigger:不再在onLeave里重复创建,而是提前初始化并设置为disabled: true,等水平滚动完成后再启用,避免实例冲突。
  2. 回滚状态重置:添加onEnterBack回调,当用户回滚时重置堆叠动画并禁用第二个ScrollTrigger,保证来回滚动的状态一致性。
  3. 布局稳定后再刷新:在onLeave里调用stackingScrollTrigger.refresh(),确保水平滚动的pin释放后,第二个ScrollTrigger能拿到正确的元素尺寸和布局位置,解决#facts消失的问题。
  4. 代码结构解耦:把两个ScrollTrigger的初始化逻辑分开,后续维护和调试更方便。

这样修改后,堆叠效果会严格在水平滚动完全结束后才触发,#facts元素也不会再莫名消失啦。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 08:13:01