基于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)回頭部,并將第一段文本的頭部內(nèi)容替換為第二段的頭部內(nèi)容,同時(shí)動(dòng)畫也回到開始位置,這樣,用戶從視覺效果上看是感受不到變化的。
以下代碼是 Marquee 組件及其樣式的簡單實(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ù)量的話,可以使用更簡便的方法來提取鍵。
提取出表頭后,我們就可以遍歷對(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;
}
}
然后,刷新頁面,再看一下頁面效果,這樣就實(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)文章
基于webpack開發(fā)react-cli的詳細(xì)步驟
這篇文章主要介紹了基于webpack開發(fā)react-cli的詳細(xì)步驟,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06
React Native仿美團(tuán)下拉菜單的實(shí)例代碼
本篇文章主要介紹了React Native仿美團(tuán)下拉菜單的實(shí)例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08
React實(shí)現(xiàn)類似于Vue中的插槽的項(xiàng)目實(shí)踐
本文主要介紹了React實(shí)現(xiàn)類似于Vue中的插槽的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
React使用Hooks從服務(wù)端獲取數(shù)據(jù)的完整指南
本文將從基礎(chǔ)到高級(jí)用法,詳細(xì)介紹如何在 React 項(xiàng)目中優(yōu)雅地使用 Hooks 進(jìn)行服務(wù)端數(shù)據(jù)獲取,涵蓋錯(cuò)誤處理、加載狀態(tài)、性能優(yōu)化等核心場景,并提供可直接復(fù)用的代碼模板,需要的朋友可以參考下2025-03-03
React?+?Typescript領(lǐng)域初學(xué)者的常見問題和技巧(最新)
這篇文章主要介紹了React?+?Typescript領(lǐng)域初學(xué)者的常見問題和技巧,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
使用React手寫一個(gè)對(duì)話框或模態(tài)框的方法示例
這篇文章主要介紹了使用React手寫一個(gè)對(duì)話框或模態(tài)框的方法示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04

