React虛擬列表的實(shí)現(xiàn)代碼
最近看了vueuse的useVirtualList的實(shí)現(xiàn)方式,發(fā)現(xiàn)虛擬滾動(dòng)效果不錯(cuò),就嘗試著同樣的寫(xiě)法改成react版本。
虛擬列表主要包含三部分組成,示意圖如下:
定義一個(gè) useVirtualList 的 hook 需要接收兩個(gè)參數(shù),一是接收所有的數(shù)據(jù),二是定義一些配置參數(shù);需要返回的四個(gè)參數(shù),一是當(dāng)前加載的列表,二是獲取容器的 ref 和容器滾動(dòng)事件,三是快速定位的方法,四是內(nèi)層容器的樣式。基本的代碼結(jié)構(gòu)如下:
export type UseVirtualListOptions = { // 如果是每條數(shù)據(jù)的高度相同為number 否則通過(guò)方法計(jì)算高度 itemHeight: number | ((index: number) => number); overscan?: number; }; export const useVirtualList = ( list: AllItems[], options: UseVirtualListOptions ): UseVirtualListReturn => { const containerRef = useRef(null); const [currentList, setCurrentList] = useState<CurrentList[]>([]); const [wrapperStyle, setWrapperStyle] = useState({ width: "100%", height: "0px", marginTop: "0px", }); return { list: currentList, containerProps: { ref: containerRef, onScroll: () => {}, }, scrollTop, wrapperStyle, }; };
因?yàn)樾枰萜鞯母叨群蜐L動(dòng)距離來(lái)計(jì)算加載的內(nèi)容,因此需要在 dom 掛載完成后執(zhí)行。
1、計(jì)算容器的的高度能顯示的數(shù)據(jù)
如果 itemHeight 類型為 number,則可以通過(guò)容器高度 / itemHeight 來(lái)計(jì)算容器能顯示多少條數(shù)據(jù),否則需要把上一次開(kāi)始索引作為起始值,預(yù)估滾動(dòng)之后能夠在可視區(qū)域顯示多少數(shù)據(jù)。
const getViewCapacity = (containerHeight: number) => { if (typeof itemHeight === "number") { return Math.ceil(containerHeight / itemHeight); } let sum = 0; let capacity = 0; const { start = 0 } = state; console.log(start); for (let i = start; i < list.length; i++) { const height = itemHeight(i); sum += height; if (sum >= containerHeight) { capacity = i; break; } } return capacity - start; };
2、計(jì)算滾動(dòng)偏移的數(shù)據(jù)量
如果 itemHeight 類型為 number,則可以通過(guò)容器高度 / itemHeight 來(lái)計(jì)算滾動(dòng)偏移的數(shù)據(jù)量,否則需要對(duì)滾動(dòng)偏移的 dom 高度進(jìn)行累加,計(jì)算出偏移的數(shù)據(jù)量。
const getOffset = (scrollTop: number) => { if (typeof itemHeight === "number") { return Math.floor(scrollTop / itemHeight) + 1; } let sum = 0; let offset = 0; for (let i = 0; i < list.length; i++) { const height = itemHeight(i); sum += height; if (sum >= scrollTop) { offset = i; break; } } return offset + 1; };
3、計(jì)算滾動(dòng)時(shí)需要加載的數(shù)據(jù)
需要加載的數(shù)據(jù)開(kāi)始位置 = 滾動(dòng)偏移量 - 預(yù)加載的數(shù)量
需要加載的數(shù)據(jù)結(jié)束位置 = 滾動(dòng)偏移量 + 可視區(qū)域顯示數(shù)據(jù)量 + 預(yù)加載數(shù)據(jù)量
如果開(kāi)始位置 < 0 則從 0 開(kāi)始,結(jié)束位置大于總數(shù)組長(zhǎng)度,則結(jié)束位置為總數(shù)據(jù)的長(zhǎng)度
const from = offset - overscan; const to = offset + viewCapacity + overscan; state.start = from < 0 ? 0 : from; state.end = to > list.length ? list.length : to; setCurrentList(() => { return list.slice(state.start, state.end).map((ele, index) => ({ data: ele, index: index + state.start, })); });
4、計(jì)算列表容器的高度和滾動(dòng)的高度
列表容器的高度 = 數(shù)據(jù)長(zhǎng)度 * 預(yù)估每條數(shù)據(jù)的高度 - 滾動(dòng)偏移的高度
因?yàn)榧虞d的數(shù)據(jù)不是每次都從 0 開(kāi)始,但每次渲染是從頂部開(kāi)始的,所以滾動(dòng)到高度大于數(shù)據(jù)的高度時(shí),數(shù)據(jù)位于可視區(qū)域的上方,此時(shí)需要設(shè)置 marginTop,讓加載的數(shù)據(jù)顯示在可視區(qū)域的頂部,但如果總高度不變,數(shù)據(jù)全部加載完成后,底部會(huì)有大面積留白。
const computedWrapperStype = () => { const offsetTop = getDistanceTop(state.start); setWrapperStyle({ width: "100%", height: `${totalHeight() - offsetTop}px`, marginTop: `${offsetTop}px`, }); };
5、添加快速定位功能
當(dāng)在輸入框數(shù)據(jù)要定位的索引值時(shí),可以根據(jù)索引值計(jì)算出可視區(qū)域上方需要偏移的高度,然后重新調(diào)用計(jì)算需要加載數(shù)據(jù)開(kāi)始、結(jié)束位置的方法,即可實(shí)現(xiàn)快速定位。
const scrollTop = (index: number) => { if (containerRef.current) { (containerRef.current as HTMLDivElement).scrollTop = getDistanceTop(index); calculateRange(); } };
6、源碼地址
https://stackblitz.com/edit/vitejs-vite-oy7obm?file=src%2FApp.tsx
到此這篇關(guān)于React虛擬列表的實(shí)現(xiàn)代碼的文章就介紹到這了,更多相關(guān)React虛擬列表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react實(shí)現(xiàn)記錄拖動(dòng)排序
這篇文章主要介紹了react實(shí)現(xiàn)記錄拖動(dòng)排序的相關(guān)資料,需要的朋友可以參考下2023-07-07一文詳解手動(dòng)實(shí)現(xiàn)Recoil狀態(tài)管理基本原理
這篇文章主要為大家介紹了一文詳解手動(dòng)實(shí)現(xiàn)Recoil狀態(tài)管理基本原理實(shí)例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05react清空ant.design中表單內(nèi)容的方法實(shí)現(xiàn)
本文主要介紹了react清空ant.design中表單內(nèi)容的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12解決antd的Table組件使用rowSelection屬性實(shí)現(xiàn)多選時(shí)遇到的bug
這篇文章主要介紹了解決antd的Table組件使用rowSelection屬性實(shí)現(xiàn)多選時(shí)遇到的bug問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08關(guān)于react ant 組件 Select下拉框 值回顯的問(wèn)題
這篇文章主要介紹了關(guān)于react ant 組件 Select下拉框 值回顯的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08React如何通過(guò)@craco/craco代理接口
這篇文章主要介紹了React如何通過(guò)@craco/craco代理接口問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10useEffect中return函數(shù)的作用和執(zhí)行時(shí)機(jī)解讀
這篇文章主要介紹了useEffect中return函數(shù)的作用和執(zhí)行時(shí)機(jī),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01ReactNative實(shí)現(xiàn)的橫向滑動(dòng)條效果
本文介紹了ReactNative實(shí)現(xiàn)的橫向滑動(dòng)條效果,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),補(bǔ)充介紹了ReactNative基于寬度變化實(shí)現(xiàn)的動(dòng)畫(huà)效果,感興趣的朋友跟隨小編一起看看吧2024-02-02