gpt4 book ai didi

javascript - 当我快速滚动时,Intersection Observer 有时会失败

转载 作者:行者123 更新时间:2023-12-03 14:28:10 24 4
gpt4 key购买 nike

我对 Intersection Observer API 有一点问题,
它对我有用,但是。
当我在网页上快速(非常快)滚动时,Intersection Observer API 有时无法检测到视口(viewport)中元素的伪装。
当我滚动缓慢/通常它适用于所有元素
观察者选项:

RootMargin: 0px 0px -40px 0px,
threshold: 0.7,
root: null
元素高度: 100px 之间和 200px有谁知道为什么?

最佳答案

Intersection Observer 在循环中运行一个异步函数,用于检查观察到的 DOM 元素的位置。它与浏览器的渲染周期相结合,尽管它发生得非常快(大多数设备为 60fps,或每 16.66 毫秒一次),但如果您移动滚动条的速度比这些检查发生的速度更快,IO API 可能不会检测到一些可见性变化。事实上,没有被检测到的元素甚至还没有被渲染。
这是有道理的,因为 IO 的主要目标是检查一个元素是否对人眼可见。根据Intersection Observer spec目的是提供一种简单且最佳的解决方案来延迟或预加载图像和列表、检测电子商务广告的可见性等。
话虽如此,Intersection Observer 是一个期待已久的功能,它解决了很多问题,但它并不适合所有用例。如果您可以接受这样一个事实,即在高速滚动情况下它不会捕获某些交叉点 - 太好了。如果没有,那么,还有其他选择。
解决方案 1:推断相交元素
处理此问题的一种方法是尝试间接推断哪些元素穿过视口(viewport)但未被 Intersection Observer 捕获。要实现它,您需要按升序为所有列表元素赋予唯一的数字属性。然后,在每个 Intersection Observer 的回调函数中,保存相交元素的最小和最大 ID。在回调结束时,调用 setTimeout(applyChanges, 150)调度一个函数,该函数将遍历 ID 介于 min 之间的所有元素和 max IO没有省略。另外,把 clearTimeout()在回调开始时,确保该函数等到 IO 处于非事件状态一小段时间。

let minId = null;
let maxId = null;
let debounceTimeout = null;

function applyChanges() {
console.log(minId, maxId);
const items = document.querySelectorAll('.item');
// perform action on elements with Id between min and max
minId = null;
maxId = null;
}

function reportIntersection(entries) {
clearTimeout(debounceTimeout);
entries.forEach(entry => {
if (entry.isIntersecting) {
const entryId = parseInt(entry.target.id);
if (minId === null || maxId === null) {
minId = entryId;
maxId = entryId;
} else {
minId = Math.min(minId, entryId);
maxId = Math.max(maxId, entryId);
}
}
});
debounceTimeout = setTimeout(applyChanges, 500);
}

const container = document.querySelector('#container');
const items = document.querySelectorAll('.item');
const io = new IntersectionObserver(reportIntersection, container);
let idCounter = 0;
items.forEach(item => {
item.setAttribute('id', idCounter++);
io.observe(item)
});
解决方案 2:限制滚动事件
不可能影响默认滚动条行为,但您可以使用自定义滚动条覆盖它。这个想法是限制滚动事件,以便容器在每个渲染周期都不能滚动超过它自己的高度。这样, IntersectionObserver将有时间捕获所有路口。虽然我建议使用现有的库来完成这项任务,但下面是它应该如何工作的粗略逻辑。
首先,定义容器在单个渲染周期内可以滚动的最大距离: let scrollPool = 500 .在每个滚动事件上,安排重置 scrollPoolrequestAnimationFrame() 中的下一次渲染期间恢复为原始值功能。接下来,检查 scrollPool小于 0如果是, return无需滚动。否则,减去 scrollDistance来自 scrollPool并添加 scrollDistance到容器的 scrollTop属性(property)。
同样,由于用户可以通过多种方式滚动页面,因此实现此解决方案可能有点过头了。但是为了描述这个想法,这里有一个 wheel 的草稿。事件:
#container { overflow: hidden; }
let scrollPool = 500;

function resetScrollPool() {
scrollPool = 500;
}

function scrollThrottle(event) {
window.requestAnimationFrame(resetScrollPool);
if (scrollPool < 0) {
return false;
}
const scrollDistance = event.deltaY * 10;
scrollPool = scrollPool - Math.abs(scrollDistance);
document.querySelector('#container').scrollTop += scrollDistance;
}

window.addEventListener('wheel', scrollThrottle);
解决方案 3:实现自定义交叉口检测
这意味着放弃 IntersectionObserver完全使用旧方法。这在性能方面肯定会更加昂贵,但您一定会捕获所有穿过视口(viewport)的元素。这个想法是添加一个滚动处理函数来循环遍历容器的所有子项,以查看它们是否越过可见区域。为了提高性能,您可能希望将此函数反跳一秒左右。可以在以下位置找到一个很好的实现: How can I tell if a DOM element is visible in the current viewport?

关于javascript - 当我快速滚动时,Intersection Observer 有时会失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61951380/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com