背景:我使用交叉观察器构建了一个无限滚动器,目的是重新使用dom节点(也称为窗口)。当div滚动离开视口的顶部时,它绝对位于div序列的末尾,反之亦然,退出底部的div则相反。
问题:如果您向上滚动太快,则将完全滚动到div上方。同时,如果您向下滚动得太快,则不会发生这种情况。无法找出原因。
Codesandbox(打开demo in fullscreen): https://codesandbox.io/s/cranky-pasteur-wckzm
代码:
/* eslint no-unused-vars: 0 */
/* eslint react-hooks/exhaustive-deps: 0 */
/* eslint no-sequences: 0 */
import React, { useEffect } from "react";
import "./styles.css";
const App = () => {
useEffect(() => {
const divs = document.querySelectorAll('.abc')
const options = {
root: null,
threshold: 0,
rootMargin: '0px'
}
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting === false) {
if (entry.boundingClientRect.top < 0 && window.pageYOffset > 320) {
console.log('element scrolled off top')
const lastDiv = entry.target.parentNode.lastElementChild
const firstDiv = entry.target.parentNode.firstElementChild
const lastDivTopAmount = Number(lastDiv.style.top.split('').slice(0, -2).join(''))
firstDiv.style.top = `${lastDivTopAmount+320}px`
firstDiv.parentNode.appendChild(firstDiv)
return
}
if (entry.boundingClientRect.bottom > 1250 && window.pageYOffset > 320) {
console.log('element scrolled off bottom')
const firstDiv = entry.target.parentNode.firstElementChild
const lastDiv = entry.target.parentNode.lastElementChild
const firstDivTopAmount = Number(firstDiv.style.top.split('').slice(0, -2).join(''))
lastDiv.style.top = `${firstDivTopAmount-320}px`
lastDiv.parentNode.prepend(lastDiv)
return
}
}
})
}, options)
divs.forEach(div => {
observer.observe(div)
})
}, [])
return (
<div>
<div className="visibleRows">
<div
style={{top: '0px', backgroundColor: 'blue', height: '320px', width: '580px', outline: '4px solid pink'}}
className="baby-div-1 abc"
/>
<div
style={{top: '320px', backgroundColor: 'red', height: '320px', width: '580px', outline: '4px solid pink'}}
className="baby-div-2 abc"
/>
<div
style={{top: '640px', backgroundColor: 'green', height: '320px', width: '580px', outline: '4px solid pink'}}
className="baby-div-3 abc"
/>
<div
style={{top: '960px', backgroundColor: 'purple', height: '320px', width: '580px', outline: '4px solid pink'}}
className="baby-div-4 abc"
/>
<div
style={{top: '1280px', backgroundColor: 'yellow', height: '320px', width: '580px', outline: '4px solid pink'}}
className="baby-div-5 abc"
/>
<div
style={{top: '1600px', backgroundColor: 'pink', height: '320px', width: '580px', outline: '4px solid pink'}}
className="baby-div-6 abc"
/>
<div
style={{top: '1920px', backgroundColor: 'orange', height: '320px', width: '580px', outline: '4px solid pink'}}
className="baby-div-7 abc"
/>
<div
style={{top:'2240px', backgroundColor: 'cyan', height: '320px', width: '580px', outline: '4px solid pink'}}
className="baby-div-8 abc"
/>
</div>
</div>
);
};
export default App;
Styles.css
.abc {
position: absolute;
height: 320px;
width: 580px;
outline: 4px solid pink;
}
仅当观察到的条目越过threshold
时才会触发IntersectionObserver回调。由于您在选项中定义的threshold
为0
(并且根为null
),因此这意味着仅当您的一个框在threshold
上移动时(即当框进入或退出视口)。
令人困惑的部分是entries.forEach(...)
不会遍历所有观察到的目标,它只会获取超过阈值的实体。
所以您的第一个条件if (entry.boundingClientRect.top < 0 && window.pageYOffset > 320) { ... }
有效,因为当框从视口顶部滚动时,可以实现这些条件。但是,您的第二个条件if (entry.boundingClientRect.bottom > 1250 && window.pageYOffset > 320) { ... }
中的条件将可能永远无法满足,因为对于931
(bottom > 1250
),视口需要为931 + 320 > 1250
高。
我不太确定如何使用IntersectionObserver的方法,因为您不能保证在需要时会超过阈值。如果顶部框以负top
开头,并且您刚刚跟踪了“视口顶部”阈值,则可以完成工作……但是在快速滚动时可能会出现问题。
对于scroll event listener,这可能是更好的工作...,或者使用Intersection Observer来复制所有div,而不是实际移动它们。”