React虚拟滚动列表

开发思路解析
  • 实现效果,永远只渲染在可视区域的DOM数据
  • 计算容器展示所有数据的时候的高度
  • 滚动计算动态显示的开始下标和结束下标
  • slice截取显示的数据, 数据整体transformY之前数据的高度
  • 瀑布流布局的情况,对不同的列计算高度,最大高度为容器高度
    滚动过程中计算可视区域到滚动容器顶部的距离有一定难度
模拟数据,固定高度50一行
  createMockData = () => {
    let data = [];
    for (var i = 1; i < 10001; i++) {
      data.push(`${i}: 第${i}个>>>>>>>>>>>>>>>>>>>>>>`)
    }
    this.setState({
      mockData: data
    })
    this.refs.scrollBody.style.height = 10000 * 50 + "px";
    const canSeeNums = Math.ceil(Number(this.refs.wrrapCpntainer.style.height.split("px")[0]) / 50) + 2;
    this.setState({
      tail: canSeeNums,
      canSeeNums: canSeeNums,    // 可展示的数量+2
      itemHeight: 50
    })
  }
滚动动态计算 head、tail,展示的节点开始结束
  handSrcoll = (e) => {
    if (e.target.scrollTop) {
      this.setState({
        head: Math.ceil((e.target.scrollTop - this.state.itemHeight + 10) / this.state.itemHeight),
        tail: Math.ceil(e.target.scrollTop / this.state.itemHeight) + 11
      })
    } else {
      this.setState({
        head: 0,
        tail: this.state.canSeeNums
      })
    }
  }

  deBounce = (fn, timeOut = 100) => {
    let setTime;
    return function (...args) {
      let flag = true;
      if (flag) {
        fn.apply(this, args);
      }
      setTime = setTimeout(() => {
        flag = false;
        clearTimeout(setTime)
      }, timeOut)
    }
  }
结构

如果不是固定高度的行,需要动态计算之前数据占据的高度,在设置transformY
给滚动加上防抖避免触发过多的判断

          <div
            style={{
              width: 300, height: 500,
              border: "1px solid #ddd",
              overflowY: "scroll", margin: "20px auto"
            }}
            ref="wrrapCpntainer"
            onScroll={this.deBounce(this.handleScroll)}
          >
            {/** 滚动容器 */}
            <div ref="scrollBody">
              <div
                style={{
                  transform:
                    `translateY(${(head * itemHeight)}px)`
                }}
              >
                {(renderData.slice(head, tail)).map((item) => (
                  <div
                    style={{
                      height: 50, textAlign: "center", borderBottom: "1px solid #ddd", lineHeight: "50px",
                    }}
                  >
                    {item}
                  </div>
                ))}
              </div>
            </div>
          </div>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容