"onScrollEnd"

385 阅读1分钟

考虑以下场景:

  • 列表内子组件为懒加载,利用Intersection Observer使元素可见时加载复杂内容,不可见时移除复杂内容。(例如:一个页面内不能有太多高分辨率的canvas元素)
  • 通过导航菜单可以快速滚动到列表内某个子组件。

如果使用element.scrollIntoView({ behavior: "auto" });,不仅跳转过程没有动画过渡,而且在 Safari 下,如果滚动的是document,定位为position: fixed的元素会闪烁一下,非常难看。

如果使用element.scrollIntoView({ behavior: "smooth" });,顺滑的滚动过程中会雨露均沾,一路上的所有子组件都会因为瞬间可见而加载复杂内容。

所以我希望在滚动开始时暂时停用Intersection Observer,等页面停止滚动后再恢复。然而并没有事件叫做scrollstartscrollend,所以只能靠scroll击鼓传花。于是又到了👴最爱的 Event Loop 时间:

IMG_0319.PNG

具体实现如下:

  const taskID = useRef(0);
  const [scrolling, setScrolling] = useState(false);
  const scrollPage = (pageID: string) => {
    const handleScroll = () => {
      cancelAnimationFrame(taskID.current);
      requestAnimationFrame(() => {
        taskID.current = requestAnimationFrame(() => {
          setScrolling(false);
          document.removeEventListener("scroll", handleScroll);
        });
      });
    };
    setScrolling(true);
    document.addEventListener("scroll", handleScroll);
    refMap.get(pageID)?.scrollIntoView({ behavior: "smooth" });
  };