无限无缝自动轮播滚动列表 IntersectionObserver实现

背景

在一个数据看板项目中,需要实现一个像 LED 屏幕一样,无限无缝轮播学生数据的组件,效果如下:

Kapture 2021-07-11 at 15.20.21.gif

“这还不简单,直接用 Swiper 啊!”
然而实在不想为了这一小功能而引入一个 4M 大小的 Npm 包,于是决定自己动手实现

思路

直接把所有数据放在 Dom 中,之后用 Css Animation 控制它们持续向上,显然是不可能的,咱们的数据可能有成千上万条
所以肯定需要 JS 的介入,来在某些时机动态地删改数据

但也不能直接定时地对数据进行 shift + push 操作,否则数据元素的位置会瞬间偏移到上方

我想到的是,把数据分为上下两块 A 和 B,让其自然向上移动,当滚动到 A、B 的交界处碰到容器上边缘(即 A 完全看不到)时,将 A -> B,B -> C,并重置动画,如下 Demo 所示:
Kapture 2021-07-11 at 15.51.30.gif

如此便实现了旧数据的删除与新数据的增加,并且视窗内的数据元素从肉眼看上去是无缝衔接的

实现方式1

思路有了,接下来就是代码的实现方式

首先我想当然地,用 CSS Animation 让容器内的数据的 top 值不断减小(负数),同时 JS 利用 setInterval 在恰好的时间节点动态地更替数据

然而重点就在于,这个“恰好的时间节点”是无法把控的,首先 JS 的定时器本就不完全精确,另外 CSS 与 JS 的定时器更不会如我们所愿地完全一致

实现方式2

既然 JS 和 CSS 不愿顺从彼此,那就只能二选一了

于是我把动画也交给 JS 来执行,让其在 window.requestAnimationFrame 中按照一定频率增加对应元素的 top 值,并在其值增加到一定节点时操作数据

如此是能呈现出预期效果,但也产生了新的问题:

  • top 值改变的频率与单位值如果小了,视觉上会出现明显的跳帧(可用 transitions 来修补过渡)
  • 由于是 JS 通过 DOM 操作实现的动画,性能成本一下高了起来,在低端机器上将卡得惨不忍睹…

最终方式

术业有专攻,看来动画还是得交给 CSS 来实现,那么问题就回到了如何保持 CSS 与 JS 改变时机的一致性
定时器已经行不通了,所以 JS 需要寻找新的方式来感知数据元素们所在的位置

于是我把目光转向了一个较冷门的 API:IntersectionObserver教程在此

在对一个 Dom 进行 observe 后,便能及时得知其可见状态的改变,因此我们可以很方便地在 A 数据由可见 -> 不可见时收到通知,进行数据更替操作
而因为交叉观察器只会在元素的可见状态改变时触发 callback,因此性能问题也无需担心

安全边际

由于 IntersectionObserver 是异步的(包括主流的 setState等也是),因此要是数据在动画重置时还没有完成更替,可就露馅了,所以我们不妨留点安全边际

例如数据块 A:10条数据,B:5条数据
在滚动到第5条数据不可见时,咱就先对前5条数据进行更替,如此数据在滚动到第10条数据不可见时肯定已经更新完毕,可以放心地重置动画,动画重置后,再对余下的10条数据进行全部更新

浏览器兼容性

image.png

绝大多数浏览器是不用担心的了
再不济,咱还有 polyfillintersection-observer

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享