ReactJS 中如何计算横向滚动容器内当前完全展示的元素序号
React 横向滚动图片计数实现方案
核心实现逻辑
判断当前完全进入滚动容器可视区域的最大图片序号,核心判定公式为:滚动容器scrollLeft + 滚动容器clientWidth >= 第N张图片offsetLeft + 第N张图片offsetWidth
满足上述条件即说明第N张图片已完全展示,遍历所有图片取最大的符合条件的序号作为当前计数即可。
完整代码示例
import { useState, useRef, useEffect, useCallback } from 'react'; // 模拟图片数据 const IMAGE_LIST = [ 'https://example.com/img1.jpg', 'https://example.com/img2.jpg', 'https://example.com/img3.jpg', 'https://example.com/img4.jpg', 'https://example.com/img5.jpg', ]; // 简单节流函数,不想引入第三方库可直接使用 const throttle = (func, delay) => { let timer = null; return function(...args) { if (!timer) { timer = setTimeout(() => { func.apply(this, args); timer = null; }, delay); } } } const ScrollImageCounter = () => { const [currentNum, setCurrentNum] = useState(3); // 初始默认最多展示3个完整图片 const containerRef = useRef(null); const total = IMAGE_LIST.length; // 计算当前完全展示的最大图片序号 const calculateVisible = useCallback(throttle(() => { const container = containerRef.current; if (!container) return; const viewRight = container.scrollLeft + container.clientWidth; const items = container.querySelectorAll('.scroll-item'); let maxIndex = 0; items.forEach((item, index) => { const itemRight = item.offsetLeft + item.offsetWidth; // 减1px容错,避免滚动时小数精度问题导致判断失误 if (viewRight >= itemRight - 1) { maxIndex = index + 1; // 转为1开头的序号 } }); if (maxIndex !== currentNum) { setCurrentNum(maxIndex); } }, 50), [currentNum]); // 组件挂载初始化计算,绑定滚动事件 useEffect(() => { const container = containerRef.current; if (!container) return; calculateVisible(); container.addEventListener('scroll', calculateVisible); return () => { container.removeEventListener('scroll', calculateVisible); }; }, [calculateVisible]); return ( <div className="wrap" style={{ maxWidth: '414px', margin: '0 auto' }}> {/* 滚动容器 */} <div ref={containerRef} className="scroll-container" style={{ overflowX: 'auto', display: 'flex', gap: '10px', // 图片间距可自行调整 padding: '10px 0', WebkitOverflowScrolling: 'touch', // 提升移动端滚动流畅度 scrollSnapType: 'x mandatory', // 滚动自动停靠,可选优化 }} > {IMAGE_LIST.map((imgSrc, index) => ( <div key={index} className="scroll-item" style={{ flexShrink: 0, width: '128px', // 可根据布局调整,保证初始最多展示3个完整元素即可 scrollSnapAlign: 'start', // 和滚动停靠配合使用 }} > <img src={imgSrc} alt={`图片${index + 1}`} style={{ width: '100%', height: 'auto', borderRadius: '8px' }} onLoad={calculateVisible} // 图片加载完成后重新计算,避免宽高获取错误 /> </div> ))} </div> {/* 计数展示 */} <div className="counter" style={{ textAlign: 'right', padding: '0 10px' }}> {currentNum}/{total} </div> </div> ); }; export default ScrollImageCounter;
注意事项
- 要适配不同移动端宽度的话,可以将图片宽度设置为
calc((100vw - 20px) / 3),其中20px为容器留白和间距总和,保证所有设备初始都最多展示3张完整图片。 - 如果滚动时存在计数跳动问题,可以适当调整判定条件的容错值,或者增加节流的延迟时长。
- 动态加载图片的场景下,必须监听图片的
onLoad事件重新触发计数计算,避免图片未加载完成时宽高为0导致的计算错误。
内容的提问来源于stack exchange,提问作者Vuledev2905
相关产品推荐
相关产品推荐

