← CSS Animation · 能力速览 / Scroll-Snap Rotator:「hello ___」滚动轮播 待审核 8 / 22
综合应用 · scroll-snap + view()

Scroll-Snap Rotator:「hello ___」滚动轮播

一个经典的滚动轮播效果(出处见 CodePen,思路来自 @jh3yy):左边 hello 钉住不动,右边一列名字竖向滚动并吸附到正中,居中那个放大变亮、其余缩小变暗——像不断切换的 hello, ___。原作用 GSAP ScrollTrigger(pin + scrub)做高亮,而这套效果的三块拼图全部是纯 CSS,无需任何 JS;其中高亮部分用到 View Timeline 页讲的 animation-timeline: view()(见 caniuse)。

1 · 三块拼图:把 GSAP 的职责拆给原生 CSS

原作把「钉住 + 吸附 + 跟随滚动高亮」都交给 GSAP,而这三件事各有一条纯 CSS 的对应写法:

拼图 原生 CSS 怎么拆
吸附 scroll-snap-align: center 给滚动容器 scroll-snap-type: y mandatory,每个 liscroll-snap-align: center,松手即吸附到容器正中。这一段原作本来就是纯 CSS,GSAP 未介入。
钉住 hello position: sticky; top: 50% 原作的 pin: true 等价于一句 position: sticky; top: 50%:列表比容器高,hello 就一路粘在视口中线。
居中高亮 animation-timeline: view() 原作那条 scrub 时间线,换成 scroll-driven animation:每个名字按自身相对容器视口的位置(view progress)插值——走到正中(进度 50%)时 opacity:1 / scale:1,两端 0.2 / 0.8。时间轴 = 滚动,天然可回退。

2 · 现场效果

在下面的框里上下滚动(触摸板 / 滚轮 / 拖滚动条都行)。虚线是容器正中,名字滚到那条线上时被「点亮」,顶部读数会同步显示当前居中的词:

3 · 关键 CSS

.scroller {
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
}
.greet {
  position: sticky;
  top: 50%;
  transform: translateY(-50%);
}
li { scroll-snap-align: center; }

@keyframes spotlight {
  0%, 100% { opacity: .18; scale: .8; }
  50%      { opacity: 1;   scale: 1;  }
}
li > span {
  transform-origin: left center;
  animation: spotlight linear both;
  animation-timeline: view();
}

为什么 snap 中心和高亮峰值刚好对齐? scroll-snap-align: centerli 停在滚动容器正中,而 view() 的进度 50% 也正是「元素中心 = 容器视口中心」那一刻——两个 50% 天然重合,所以吸附到位时高亮恰好满格。列表首尾各垫了半个容器高的空白(::before / ::after),否则第一个和最后一个名字滚不到正中。

fill 用 both 不是 backwards 名字在进入高亮区间之前(上方)要保持 0% 帧的暗态,离开之后(下方)也要保持 100% 帧的暗态——两端都得撑住,所以用 both。对比 view-timeline 页的 spring-box:那是「进来才动、之后无所谓」,用 backwards 即可。

对照原作: GSAP 版用 ScrollTrigger { pin:true, scrub:true } 钉住 + 把时间线绑到滚动;纯 CSS 把它拆成 position: sticky(钉)+ animation-timeline: view()(绑滚动)。代价只有兼容性:view() 需要 Chromium 115+ / Firefox(带 flag),老浏览器靠 @supports 降级成「不高亮但能吸附」的普通列表;要全平台覆盖就保留 GSAP。