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的高度計算;
對緩存結果的算法查詢;
對未緩存結果的算法查詢;
根據渲染結果動態更新列表項的高度;
數據源更新時盡量范圍小的刪除失效緩存;
。。。
優化之路永無盡頭;