fix: 用 IntersectionObserver 替代 scroll 事件实现瀑布流
scroll 事件在某些布局下不触发,改用 IntersectionObserver 监听列表底部哨兵元素,进入视口时自动加载下一页,更可靠。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -115,8 +115,6 @@ export default function MonitoringView() {
|
|||||||
const [loadingMore, setLoadingMore] = useState(false);
|
const [loadingMore, setLoadingMore] = useState(false);
|
||||||
const [showBackToTop, setShowBackToTop] = useState(false);
|
const [showBackToTop, setShowBackToTop] = useState(false);
|
||||||
const PAGE_SIZE = 50;
|
const PAGE_SIZE = 50;
|
||||||
const listEndRef = useRef<HTMLDivElement>(null);
|
|
||||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
const departments = filterOptions.departments;
|
const departments = filterOptions.departments;
|
||||||
const plateNumbers = filterOptions.plates;
|
const plateNumbers = filterOptions.plates;
|
||||||
@@ -178,26 +176,34 @@ export default function MonitoringView() {
|
|||||||
loadFirstPage();
|
loadFirstPage();
|
||||||
}, [loadFirstPage]);
|
}, [loadFirstPage]);
|
||||||
|
|
||||||
// 滚动触底检测 + 回到顶部按钮
|
// 触底检测:用 IntersectionObserver 监听哨兵元素
|
||||||
const loadMoreRef = useRef(loadMore);
|
const loadMoreRef = useRef(loadMore);
|
||||||
loadMoreRef.current = loadMore;
|
loadMoreRef.current = loadMore;
|
||||||
|
const sentinelRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const sentinel = sentinelRef.current;
|
||||||
|
if (!sentinel) return;
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
if (entries[0].isIntersecting) {
|
||||||
|
loadMoreRef.current();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ rootMargin: '200px' }
|
||||||
|
);
|
||||||
|
observer.observe(sentinel);
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 回到顶部按钮
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
const scrollY = window.scrollY || document.documentElement.scrollTop;
|
const scrollY = window.scrollY || document.documentElement.scrollTop;
|
||||||
setShowBackToTop(scrollY > 400);
|
setShowBackToTop(scrollY > 400);
|
||||||
const scrollHeight = document.documentElement.scrollHeight;
|
|
||||||
const clientHeight = window.innerHeight;
|
|
||||||
if (scrollHeight - scrollY - clientHeight < 300) {
|
|
||||||
loadMoreRef.current();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||||
document.addEventListener('scroll', handleScroll, { passive: true });
|
return () => window.removeEventListener('scroll', handleScroll);
|
||||||
return () => {
|
|
||||||
window.removeEventListener('scroll', handleScroll);
|
|
||||||
document.removeEventListener('scroll', handleScroll);
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const scrollToTop = () => {
|
const scrollToTop = () => {
|
||||||
@@ -802,7 +808,8 @@ export default function MonitoringView() {
|
|||||||
<span className="text-[10px] font-bold text-slate-300">已加载全部 {total} 条</span>
|
<span className="text-[10px] font-bold text-slate-300">已加载全部 {total} 条</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div ref={listEndRef} />
|
{/* 哨兵元素:进入视口时触发加载更多 */}
|
||||||
|
<div ref={sentinelRef} className="h-1" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 回到顶部按钮 */}
|
{/* 回到顶部按钮 */}
|
||||||
|
|||||||
Reference in New Issue
Block a user