• 虚拟列表的渐进式实现(vue,react)


    1、地址(vue)

    2、react (下面是几个版本 样式在最后面)

    /*
     * @Author: your name
     * @Date: 2020-03-13 11:11:23
     * @LastEditTime: 2020-05-13 16:39:01
     * @LastEditors: Please set LastEditors
     * @Description: 列表项等宽
     * @FilePath: /optimus/src/pages/test/Index/hooks.js
     */
    import React, { useState, useEffect, useRef } from "react";
    import styles from "./style.less";
    
    const arr = [];
    for (let index = 0; index < 200; index++) {
      arr.push({
        index,
        height: parseInt(Math.random() * 30 + 20)
      });
    }
    
    const Index = (props) => {
      const [height, setHeight] = useState(0);
      const [itemHeight] = useState(30);
    
      const [totalList] = useState(arr);
      const [list, setList] = useState([]);
    
      const couterRef = useRef();
      const totalRef = useRef();
    
      useEffect(() => {
        setHeight(totalList.length * itemHeight);
      }, [totalList]);
    
      useEffect(() => {
        updateVisibleData();
      }, [totalList]);
    
      const onScrollCapture = (view) => {
        const scrollTop = totalRef.current.scrollTop;
        updateVisibleData(scrollTop);
      };
    
      const updateVisibleData = (scrollTop) => {
        scrollTop = scrollTop || 0;
    
        const clientHeight = totalRef.current.clientHeight;
        const visibleCount = Math.ceil(clientHeight / itemHeight);
        const start = Math.floor(scrollTop/ itemHeight);
        const end = start + visibleCount;
    
        const _list = totalList.slice(start, end);
        setList(_list);
    
        couterRef.current.style.webkitTransform = `translate3d(0, ${start * itemHeight}px, 0)`;
      };
    
      return (
        <div className={styles.wrap}>
          <div
            className={styles.listView}
            ref={totalRef}
            onScrollCapture={onScrollCapture}
          >
            <div
              className={styles.listViewPhantom}
              style={{ height: height }}
            ></div>
    
            <div className={styles.listViewContent} ref={couterRef}>
              {list.map((item, index) => {
                return (
                  <div
                    className={styles.listViewItem}
                    style={{ height: itemHeight }}
                    key={index}
                  >
                    {item.index}
                  </div>
                );
              })}
            </div>
          </div>
        </div>
      );
    };
    
    export default Index;
    /*
     * @Author: x
     * @Date: 2020-03-13 11:11:23
     * @LastEditTime: 2020-05-13 16:34:47
     * @LastEditors: Please set LastEditors
     * @Description: react 列表项不等宽
     * @FilePath: /optimus/src/pages/test/Index/hooks.js
     */
    import React, { useState, useEffect, useRef } from "react";
    import styles from "./style.less";
    
    const arr = [];
    for (let index = 0; index < 200; index++) {
      arr.push({
        index,
        height: parseInt(Math.random() * 30 + 20)
      });
    }
    
    const Index = (props) => {
      const [height, setHeight] = useState(0);
    
      const [totalList, setTotalList] = useState(arr);
      const [list, setList] = useState([]);
    
      const couterRef = useRef();
      const totalRef = useRef();
    
      useEffect(() => {
        let total = 0;
        let index = 0;
        let length = totalList.length;
        for (index; index < length; index++) {
          total += totalList[index].height;
        }
        setHeight(total);
      }, [totalList]);
    
      useEffect(() => {
        updateVisibleData();
      }, [totalList]);
    
      const onScrollCapture = (view) => {
        const scrollTop = totalRef.current.scrollTop;
        updateVisibleData(scrollTop);
      };
    
      const updateVisibleData = (scrollTop) => {
        scrollTop = scrollTop || 0;
    
        const start = findNearestItemIndex(scrollTop);
        const end = findNearestItemIndex(scrollTop + totalRef.current.clientHeight);
    
        const _list = totalList.slice(start, Math.min(end + 1, totalList.length));
    
        setList(_list);
    
        couterRef.current.style.webkitTransform = `translate3d(0, ${getItemSizeAndOffset(start).offset}px, 0)`;
      };
    
      const findNearestItemIndex = scrollTop => {
        let total = 0;
        for (let i = 0, j = totalList.length; i < j; i++) {
          const size = totalList[i].height;
          total += size;
          if (total >= scrollTop || i === j -1) {
            return i;
          }
        }
        return 0;
      }
    
      const getItemSizeAndOffset = start => {
        let total = 0;
        for (let i = 0, j = Math.min(start, totalList.length - 1); i <= j; i++) {
          const size = totalList[i].height;
       
          if (i === j) {
            return {
              offset: total,
              size
            };
          }
          total += size;
        }
    
        return {
          offset: 0,
          size: 0
        };
      }
      
    
      return (
        <div className={styles.wrap}>
          <div
            className={styles.listView}
            ref={totalRef}
            onScrollCapture={onScrollCapture}
          >
            <div
              className={styles.listViewPhantom}
              style={{ height: height }}
            ></div>
    
            <div className={styles.listViewContent} ref={couterRef}>
              {list.map((item, index) => {
                return (
                  <div
                    className={styles.listViewItem}
                    style={{ height: item.height }}
                    key={index}
                  >
                    {item.index}
                  </div>
                );
              })}
            </div>
          </div>
        </div>
      );
    };
    
    export default Index;
    .wrap{
      width: 100%;
      height: 800px;
      display: flex;
      justify-content: center;
      align-items: center;
      background-color: antiquewhite;
    }
    
    .listView {
      height: 400px;
      width: 160px;
      overflow: auto;
      position: relative;
      border: 1px solid #aaa;
    }
    
    .listViewPhantom {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      z-index: 100;
      width: 160px;
      background-color: rgba(red,0.5);
    }
    
    .listViewContent {
      left: 0;
      right: 0;
      top: 0;
      position: absolute;
    }
     
    .listViewItem {
      padding: 5px;
      color: #666;
      box-sizing: border-box;
      border-bottom: 1px solid red;
      display: flex;
      align-items: center;
    }

    写在最后

      题目当然还可以再优化:

        对itemHeight的缓存;

        对contextHeight的高度计算;

        对缓存结果的算法查询;

        对未缓存结果的算法查询;

        根据渲染结果动态更新列表项的高度;

        数据源更新时尽量范围小的删除失效缓存;  

        。。。

      优化之路永无尽头;

  • 相关阅读:
    javascript keycode大全
    在WEB环境下打印报表的crystal的解决方案
    Trim()
    C#应用结构体变量
    锚点定位
    C# 按地址传值
    [GIIS]JS 图片 Preview
    c# 模拟网站登陆
    此数据库没有有效所有者,因此无法安装数据库关系图支持对象" 解决方法
    风讯.NET与NETCMS的选择—开源.NET内容管理系统
  • 原文地址:https://www.cnblogs.com/webcabana/p/12883219.html
Copyright © 2020-2023  润新知