写了三年滚动动画,今天才发现 scroll-driven 和 scroll-triggered 根本不是一回事

写过滚动动画的人都踩过这个坑——要写一个卡片进视口就淡入,下意识就 npm install AOS 或者写 IntersectionObserver。Chrome 146+ 其实给了两个原生 API,一个叫 scroll-driven,一个叫 scroll-triggered。名字差不多,机制完全不同。

scroll-driven(滚动驱动):动画进度直接绑定滚动位置。你滚到哪,动画就到哪;你往回滚,动画就倒放。像视频播放器的进度条——停在哪帧,画面就卡在哪帧。

scroll-triggered(滚动触发):滚动跨越一个门槛,触发一次动画,然后这个动画按自己的时间轴跑完,不管你之后怎么滚。像按了播放键——它自己跑,不 care 进度条。

选错了会怎样?在一个应该进门就跑完的场景用了 scroll-driven,快速滚动的用户会看到动画永远跟不上——元素已经滚走了,透明度才到 30%。反过来,在一个需要跟着滚动走的场景用了 scroll-triggered,用户停下来阅读时动画就卡住,感觉像是页面卡了。

scroll-driven 的典型写法:

.card { animation: fade-up linear both; animation-timeline: view(); animation-range: entry 0% cover 30%; }

animation-timeline: view() 让动画绑定元素在视口里的位置。animation-range: entry 0% cover 30% 的意思是:元素从完全不可见(entry 0%)到被视口覆盖 30%(cover 30%)这段区间内,动画从 0% 跑到 100%。出了这个区间,动画停在终点或起点。往回滚动,动画会倒放。

适合的场景:阅读进度条、视差背景、sticky + 滚动吸附、图表随滚动绘制。

scroll-triggered 的典型写法:

.card { animation: fade-up 0.5s ease-out both; timeline-trigger: –t view() entry 100% exit 0%; animation-trigger: –t play-forwards; }

timeline-trigger: –t view() entry 100% exit 0% 定义了触发门槛:元素底部进入视口(entry 100%)到顶部离开视口(exit 0%)。animation-trigger: –t play-forwards 说触发一次就按 0.5 秒时间轴正常播完。一旦触发,用户滚动到哪都不影响。

适合的场景:卡片入场淡入、数字递增动画、导航激活态、步骤进度动画。

两者的实际区别:

快速滚动时,scroll-driven 版本的卡片透明度会跟着滚动位置实时变化;scroll-triggered 版本,一旦进入门槛就按自己的 0.5 秒节奏淡入,不受滚动速度影响。

还有一个现实差距:scroll-triggered 目前只有 Chrome 146+,Safari 和 Firefox 还没上。生产环境里 scroll-driven 兼容性更好,scroll-triggered 可以当渐进增强。

核心结论:

scroll-driven 问的是动画进度和滚动位置是什么关系,scroll-triggered 问的是滚动跨越门槛时触发什么动作。选对了,代码简洁,效果符合预期;选错了,动画表现和设计意图完全相反。下次写滚动动画之前,先问自己一个问题:需要动画跟着滚动位置实时走,还是触发一次就跑完?

评论区

0 条评论

登录后可评论。

小鹿·界面实验室 844 阅读