大家好,我是 ConardLi。
从 Chrome 129
开始,我们可以在 JavaScript 中使用 scrollSnapChange
和 scrollSnapChanging
事件,这两个事件可以让我们实现非常棒的滚动动画效果,下面我们一起来学习一下。
注意:本文中的 GIF 都经过压缩,实际的动画效果相当丝滑!
在 scrollSnapChange
出现之前,如果你想知道哪个元素被快照到了滚动视口里,你可以使用交叉观察器(Intersection Observer
)来检测。但要确定哪个元素被快照是有局限性的,只有在某些特定情况下才能使用这种方法。
比如说,你可以检测滚动视口(scroll port
)是否被快照元素填满或者大部分填满。具体做法是,观察滚动区域内哪些元素在相交,然后根据哪个元素占用了大部分的滚动区域,假设这个元素是快照目标。接下来,你再等待 scrollend
事件触发后,对这个被快照的目标元素进行操作。
然而,在 scrollSnapChanging
出现之前,知道捕捉目标何时改变或者它被改变成什么是不可能的。
好消息是,这些新的事件让我们可以很容易的货渠道这些信息。这使得滚动快照的交互能够突破当前的能力,实现更加广泛的应用,并能够编排滚动快照关系和引入新的用户界面反馈场景。
scrollSnapChange
这个事件只有在滚动手势导致新的快照目标停留时才会触发,并按以下顺序进行:
- 滚动停止后触发;
- 在
scrollend
之前。
具体来说,这个事件会在滚动完成且捕捉到新的快照目标时,在 scrollend
之前触发。这使得事件看起来像是懒触发或即时触发的,因为它会在滚动手势完成时触发。
js
scroller.addEventListener('scrollsnapchange', event => {
console.log(event.snapTargetBlock);
console.log(event.snapTargetInline);
})
scroller.onscrollsnapchange = event => {
console.log(event.snapTargetBlock);
console.log(event.snapTargetInline);
}
这个事件将捕捉到的快照元素通过事件对象的 snapTargetBlock
和 snapTargetInline
属性暴露出来。如果滚动方向只有水平的,那么 snapTargetBlock
属性会是 null
。这些属性的值会是元素节点。
关于 scrollSnapChange
的一些细节:
-
用户释放手势后才触发: 当用户的手指还在屏幕上或者手指还在触控板上时,表示用户的手势还未完成滚动,这意味着滚动还未结束,因此快照目标还未改变,它等待用户手势完全完成后才触发。
-
如果快照目标未改变,不会触发 : 这个事件是专为快照变化设计的,如果快照目标没有改变,事件不会触发,即便用户进行了滚动操作。用户确实进行了滚动,所以在滚动完成后仍会触发
scrollend
事件。
scrollSnapChanging
一旦浏览器认为滚动手势已经或将要产生新的抓取目标,该事件就会触发,它会在滚动过程中紧急触发。
js
scroller.addEventListener('scrollsnapchanging', event => {
console.log(event.snapTargetBlock);
console.log(event.snapTargetInline);
})
scroller.onscrollsnapchanging = event => {
console.log(event.snapTargetBlock);
console.log(event.snapTargetInline);
}
这个事件将捕捉到的快照元素通过事件对象的 snapTargetBlock
和 snapTargetInline
属性暴露出来。如果滚动方向只有垂直的,那么 snapTargetInline
属性会是 null
。这些属性的值会是元素节点。
关于 scrollSnapChanging
的一些细节:
-
在滚动手势过程中频繁触发 : 与
scrollSnapChange
在用户完成整个滚动手势后才触发不同,scrollSnapChanging
在用户用手指或触控板滚动时会频繁且提前触发。假设用户在不抬起手指的情况下慢慢滚动,只要用户在多个潜在的快照目标上方平移,这个事件会在手势过程中多次触发。每次用户滚动时,如果浏览器确定在释放时会停留在新的快照目标上,事件就会触发,告诉你是哪个元素。 -
不会对所有经过的快照目标触发: 考虑一种甩动手势,用户的滚动甩动动作一次性跨越多个快照目标时,这个事件会针对最终停留的目标触发一次。因此,它是积极触发的,但不会过度浪费资源,在你需要时尽早提供快照目标信息。
使用示例
这些事件不仅开启了许多新的使用场景,还使当前的一些动画效果实现起来更加简单。一个明显的例子是启用快照触发动画。在上下文中,当某个元素成为快照目标时,可以展示该元素、其子元素或相关信息。
下面展示了一些使用场景,帮助你立即提高工作效率。
聚焦评论
这个例子会推广或视觉上聚焦于快照到的评论。以下是实现的方法:
js
scroller.onscrollsnapchange = event => {
scroller.querySelector(':scope .snapped')?.classList.remove('snapped')
event.snapTargetInline.classList.add('snapped')
}
展示捕捉元素的标题
在这个示例中,包含了两个事件 scrollSnapChange
和 scrollSnapChangin
g,让你可以看到它们的触发时机和用户体验的差异。
幻灯片
通过这个例子能够知道何时一个新的幻灯片停留在目标位置,并在此刻对其内容进行一次动画处理。
js
document.addEventListener('scrollsnapchange', event => {
event.snapTargetBlock.classList.add('seen')
})
在滚动条中捕捉 x轴 和 y轴
滚动捕捉也可以适用于允许水平和垂直滚动的滚动条,当我们在网格上滚动时,这个演示同时显示了 scrollSnapChanging
和 scrollSnapChange
。另外这个例子也说明了浏览器捕捉到的元素可能并不总是我们认为的那个元素。
虚线边框表示
scrollSnapChanging
目标,实线边框表示scrollSnapChange
目标。红色代表snapTargetInline
,蓝色代表snapTargetBlock
。
双链接滚动条
在这个例子中有两个滚动捕捉容器,其中一个是二级链接列表,另一个是实际的分页内容。新的 scrollSnapChanging
事件可以让双向链接这种捕捉状态变得很简单,它们可以始终保持同步。
OKLCH 颜色选择器
在这个例子中有 3 个滚动条,每个滚动条代表 OKLCH
中的不同颜色通道。捕捉的元素与其相关的单选组同步,并且可以从包装输入的表单中检索数据。对于鼠标或触摸用户,我们可以滚动到所需的值。对于键盘用户,可以使用 Tab 键并使用箭头键。
阶梯式动画
这个例子中通过使用 scrollsnapchange
事件触发的过渡效果,逐步增强滚动快照体验。
使用以下代码来检查事件支持情况:
js
if ('onscrollsnapchange' in window) {
// ok to use snap change
}
![](https://files.mdnice.com/user/6267/bd3f7243-c80d-4fe2-8c20-14cd453c57e1.gif)
可滚动标尺
这个例子展示了一个可滚动的标尺,作为选择数值输入的一种替代方式。用户可以直接在数字输入框中输入数值,也可以通过滚动来选择长度。在用户手势进行中使用 input
事件清除选择,而在 change
事件中更新状态并确认用户的选择。
Cover Flow 滚动动画
这个例子基于 Bramus Van Damme
出色的滚动驱动动画,复现了著名的 macOS Cover Flow
效果。独特之处在于使用 scrollSnapChanging
来隐藏专辑标题,并使用 scrollSnapChange
来展示新标题。
最后
大家觉得这个事件怎么样?欢迎在评论区留言。
抖音前端架构团队目前放出不少新的 HC ,又看起会的小伙伴可以看看这篇文章:抖音前端架构团队正在寻找人才! FE/Client/Server/QA,25 届校招同学可以直接用内推码:DRZUM5Z
,或者加我微信联系。
如果你想加入高质量前端交流群,或者你有任何其他事情想和我交流也可以添加我的个人微信 ConardLi 。