基于React實(shí)現(xiàn)無限滾動(dòng)表格
無限滾動(dòng)效果
以文本為例,為了實(shí)現(xiàn)無限循環(huán)的視覺效果,我們需要準(zhǔn)備兩段相同的文本,并讓第二段文本的頭部銜接在第一段文本的尾部。同時(shí),為兩段文本設(shè)置相同的滾動(dòng)動(dòng)畫。
當(dāng)?shù)谝欢挝谋緷L動(dòng)到尾部時(shí),如果能讓第一段文本的位置瞬間移動(dòng)回頭部,并將第一段文本的頭部?jī)?nèi)容替換為第二段的頭部?jī)?nèi)容,同時(shí)動(dòng)畫也回到開始位置,這樣,用戶從視覺效果上看是感受不到變化的。
以下代碼是 Marquee
組件及其樣式的簡(jiǎn)單實(shí)現(xiàn)。
// Marquee.tsx export default function Marquee(props) { const { children } = props; return ( <div className={styles.marquee}> <div className={styles.ctx}>{children}</div> <div className={styles.ctx}>{children}</div> </div> ); } // App.tsx export default function App() { return ( <Marquee>只期待 後來的你 能快樂 那就是 後來的我 最想的</Marquee> ); }
// Marquee.module.scss .marquee { width: 40px; overflow: hidden; position: relative; .ctx { animation: scroll 4s infinite linear; // 指定滾動(dòng)動(dòng)畫 } } @keyframes scroll { 0% { transform: translateY(0); } 100% { transform: translateY(-100%); } }
懸停效果
實(shí)現(xiàn)鼠標(biāo)懸停效果,通常有兩種方法:
- 聲明
isHovered
變量,通過onMouseEnter
和onMouseLeave
事件改變isHovered
的值,從而實(shí)現(xiàn)懸停效果。 - 使用
:hover
偽類,通過設(shè)置animation-play-state
屬性控制動(dòng)畫的運(yùn)行,從而實(shí)現(xiàn)懸停效果。
本文使用第二種方法,為整個(gè)外層容器設(shè)置 :hover
樣式。當(dāng)鼠標(biāo)懸浮在整個(gè)容器上時(shí),讓內(nèi)部元素的動(dòng)畫暫停。我們?cè)?Marquee.module.scss
文件中添加如下樣式:
// ... .marquee:hover { .ctx { animation-play-state: paused; } }
到此,最基本的功能已經(jīng)實(shí)現(xiàn),接下來開始實(shí)現(xiàn)表格組件的無限滾動(dòng)。
表格的無限滾動(dòng)
首先,實(shí)現(xiàn)根據(jù)配置動(dòng)態(tài)生成表格的功能。我們從傳入的對(duì)象數(shù)組中提取所有的鍵,作為表頭內(nèi)容。
本文實(shí)現(xiàn)的表頭提取函數(shù)考慮了傳入可選參數(shù)的情況,因此它會(huì)遍歷所有列表項(xiàng)并提取所有存在的鍵。如果表頭屬性是固定數(shù)量的話,可以使用更簡(jiǎn)便的方法來提取鍵。
提取出表頭后,我們就可以遍歷對(duì)象數(shù)組中的每一項(xiàng),并根據(jù)對(duì)應(yīng)的鍵將屬性值填入相應(yīng)的單元格中,逐步構(gòu)建起表格內(nèi)容。
基于上述步驟分析,表格組件的初步實(shí)現(xiàn)如下(樣式在文章末尾給出):
// DataTable.tsx export default function DataTable<T extends object>({ dataSource }: { dataSource: Array<T> }) { if (!Array.isArray(dataSource) || dataSource.length === 0) { return null; // 數(shù)據(jù)源不是數(shù)組或數(shù)組為空,停止操作 } const labelList = dataSource.reduce((acc: any, cur: any) => { const keys = Object.keys(cur); for (const key of keys) { if (!acc.includes(key)) { acc.push(key); } } return acc; }, []); // 提取表頭 return ( <table> <thead> <tr> {labelList.map((key, index) => ( <th key={index}>{key}</th> ))} </tr> </thead> <tbody> {dataSource.map((data, rowIndex) => ( <tr key={rowIndex}> {labelList.map((key, columnIndex) => ( <!-- 如果是可選屬性,賦予默認(rèn)值 --> <td key={columnIndex}>{data[key] ?? '-'}</td> ))} </tr> ))} </tbody> </table> ); }
此時(shí),在 App.tsx
中直接引用 DataTable
組件并傳入 data
數(shù)組,就可以看到表格動(dòng)態(tài)生成如下。
// App.tsx import DataTable from '@/components/DataTable'; export default function App() { const data = [ { name: 'Alice', city: 'New York' }, { name: 'Bob', age: 30, city: 'San Francisco' }, { name: 'Charlie', age: 35, city: 'Chicago' }, { name: 'David', age: 40, city: 'Los Angeles', num: 0 }, ]; return ( <DataTable dataSource={data} /> ); }
接下來,就是加上無限滾動(dòng)的效果。
通常來說,可滾動(dòng)表格只會(huì)有一個(gè)表頭,也就是只有一個(gè) thead
部分,并且此處我們把它固定在頂部。再按照上述文字滾動(dòng)的實(shí)現(xiàn)思路,我們只要在一個(gè) table
中準(zhǔn)備兩份相同的 tbody
內(nèi)容,然后給這兩份 tbody
設(shè)置相同的動(dòng)畫即可。
同時(shí),為了限制整體容器的高度,以防用戶在表格位置滾動(dòng)時(shí)感知到突變,造成不好的視覺體驗(yàn),還需要給 table
加一層外部容器。
到此,整個(gè)分析過程就結(jié)束了,DataTable
組件的最終實(shí)現(xiàn)如下:
// DataTable.tsx export default function DataTable<T extends object>({ dataSource }: { dataSource: Array<T> }) { if (!Array.isArray(dataSource) || dataSource.length === 0) { return null; // 數(shù)據(jù)源不是數(shù)組或數(shù)組為空,停止操作 } const labelList = dataSource.reduce((acc: any, cur: any) => { const keys = Object.keys(cur); for (const key of keys) { if (!acc.includes(key)) { acc.push(key); } } return acc; }, []); // 提取表頭 return ( <div className={styles.container}> <table className={styles.table}> <thead> <tr> {labelList.map((key, index) => ( <th key={index}>{key}</th> ))} </tr> </thead> <tbody className={styles.tbody}> {dataSource.map((data, rowIndex) => ( <tr key={rowIndex}> {labelList.map((key, columnIndex) => ( <td key={columnIndex}>{data[key] ?? '-'}</td> ))} </tr> ))} </tbody> <tbody className={styles.tbody}> {dataSource.map((data, rowIndex) => ( <tr key={rowIndex}> {labelList.map((key, columnIndex) => ( <td key={columnIndex}>{data[key] ?? '-'}</td> ))} </tr> ))} </tbody> </table> </div> ); }
// DataTable.module.scss @keyframes scroll { 0% { transform: translateY(0); } 100% { transform: translateY(-100%); } } .container { height: 160px; overflow: hidden; .table { height: 100%; overflow: hidden; position: relative; background-color: #e6e6e6; .tbody { animation: scroll 4s infinite linear; } } } table { width: max-content; border-spacing: 0; thead { height: 30px; z-index: 99; background-color: #adbcaa; position: sticky; top: 0; tr { color: #fff; font-weight: bold; } } th, td { width: max-content; padding: 0 8px; line-height: 30px; text-align: center; } }
然后,刷新頁(yè)面,再看一下頁(yè)面效果,這樣就實(shí)現(xiàn)了。
最后
以上就是基于React實(shí)現(xiàn)無限滾動(dòng)表格的詳細(xì)內(nèi)容,更多關(guān)于React無限滾動(dòng)表格的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
如何去除富文本中的html標(biāo)簽及vue、react、微信小程序中的過濾器
這篇文章主要介紹了如何去除富文本中的html標(biāo)簽及vue、react、微信小程序中的過濾器,在vue及react中經(jīng)常會(huì)遇到,今天通過實(shí)例代碼給大家講解,需要的朋友可以參考下2018-11-11React使用useImperativeHandle自定義暴露給父組件的示例詳解
useImperativeHandle?是?React?提供的一個(gè)自定義?Hook,用于在函數(shù)組件中顯式地暴露給父組件特定實(shí)例的方法,本文將介紹?useImperativeHandle的基本用法、常見應(yīng)用場(chǎng)景,需要的可以參考下2024-03-03Component與PureComponent對(duì)比解析
這篇文章主要為大家介紹了Component與PureComponent解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03