You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 18:39:03