考虑以下场景:
- 列表内子组件为懒加载,利用
Intersection Observer使元素可见时加载复杂内容,不可见时移除复杂内容。(例如:一个页面内不能有太多高分辨率的canvas元素) - 通过导航菜单可以快速滚动到列表内某个子组件。
如果使用element.scrollIntoView({ behavior: "auto" });,不仅跳转过程没有动画过渡,而且在 Safari 下,如果滚动的是document,定位为position: fixed的元素会闪烁一下,非常难看。
如果使用element.scrollIntoView({ behavior: "smooth" });,顺滑的滚动过程中会雨露均沾,一路上的所有子组件都会因为瞬间可见而加载复杂内容。
所以我希望在滚动开始时暂时停用Intersection Observer,等页面停止滚动后再恢复。然而并没有事件叫做scrollstart或scrollend,所以只能靠scroll击鼓传花。于是又到了👴最爱的 Event Loop 时间:
具体实现如下:
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" });
};