SOURCE

console 命令行工具 X clear

                    
>
console
gsap.registerPlugin(ScrollTrigger);

const cards = gsap.utils.toArray('.card');
const overlapBase = 80; // 堆叠起始left
const overlapGap = 40;  // 堆叠间隔
const overlapStep = 400; // 每张卡片动画的滚动距离

cards.forEach((card, i) => {
  // 每一段滚动区间,前i+1张卡片都要到堆叠区
  ScrollTrigger.create({
    trigger: ".fake-scroll",
    start: `${i * overlapStep} top`,
    end: `${(i + 1) * overlapStep} top`,
    scrub: true,
    onUpdate: self => {
      cards.forEach((c, j) => {
        if (j <= i) {
          // 已经叠加的卡片
          gsap.to(c, {
            left: overlapBase + overlapGap * j + "px",
            duration: 0.1,
            overwrite: 'auto'
          });
        } else {
          // 还没叠加的卡片,保持原位
          gsap.to(c, {
            left: 400 * j + "px",
            duration: 0.1,
            overwrite: 'auto'
          });
        }
      });
    }
  });
});
<div class="main-wrapper">
  <div class="cards-container">
    <div class="card">卡片1</div>
    <div class="card">卡片2</div>
    <div class="card">卡片3</div>
  </div>
</div>
<div class="fake-scroll"></div>
body {
  margin: 0;
  background: #181818;
  overflow-x: hidden;
}
.main-wrapper {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
.cards-container {
  position: relative;
  width: 500px;
  height: 350px;
}
.card {
  position: absolute;
  top: 0;
  width: 300px;
  height: 350px;
  background: #fff;
  border-radius: 24px;
  box-shadow: 0 8px 40px rgba(0,0,0,0.15);
  font-size: 2.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  left: 0;
  z-index: 1;
  transition: box-shadow 0.3s;
}
.card:nth-child(1) { left: 0; z-index: 1;}
.card:nth-child(2) { left: 400px; z-index: 2;}
.card:nth-child(3) { left: 800px; z-index: 3;}
.fake-scroll { height: 1800px; }

本项目引用的自定义外部资源