基于 React 实现无限滚动表格组件

文字的无限滚动

无限滚动效果

为了实现无限滚动的视觉效果,我们需要准备两段相同的文本,并让第二段文本的头部衔接在第一段文本的尾部。同时,为两段文本设置相同的滚动动画。

当第一段文本滚动到尾部时,如果能让第一段文本的位置瞬间移动回头部,并将第一段文本的头部内容替换为第二段的头部内容,同时动画也回到开始位置,这样,用户从视觉效果上看是感受不到变化的。

以下代码是 Marquee 组件及其样式的简单实现。

// 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; // 指定滚动动画
  }
}

@keyframes scroll {
  0% {
    transform: translateY(0);
  }
  100% {
    transform: translateY(-100%);
  }
}

动画.gif

悬停效果

实现鼠标悬停效果,通常有两种方法:

  1. 声明 isHovered 变量,通过 onMouseEnteronMouseLeave 事件改变 isHovered 的值,从而实现悬停效果。
  2. 使用 :hover 伪类,通过设置 animation-play-state 属性控制动画的运行,从而实现悬停效果。

本文使用第二种方法,为整个外层容器设置 :hover 样式。当鼠标悬浮在整个容器上时,让内部元素的动画暂停。我们在 Marquee.module.scss 文件中添加如下样式:

// Marquee.module.scss
.marquee:hover {
  .ctx {
    animation-play-state: paused;
  }
}

动画.gif
到此,最基本的功能已经实现,接下来开始实现表格组件的无限滚动。

表格的无限滚动

首先,实现根据配置动态生成表格的功能。我们从传入的对象数组中提取所有的键,作为表头内容。

本文实现的表头提取函数考虑了传入可选参数的情况,因此它会遍历所有列表项并提取所有存在的键。如果表头属性是固定数量的话,可以使用更简便的方法来提取键。

提取出表头后,我们就可以遍历对象数组中的每一项,并根据对应的键将属性值填入相应的单元格中,逐步构建起表格内容。

基于上述步骤分析,表格组件的初步实现如下(样式在文章末尾给出):

// DataTable.tsx
export default function DataTable<T extends object>({ dataSource }: { dataSource: Array<T> }) {
  if (!Array.isArray(dataSource) || dataSource.length === 0) {
    return null; // 数据源不是数组或数组为空,停止操作
  }

  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) => (
              <!-- 如果是可选属性,赋予默认值 -->
              <td key={columnIndex}>{data[key] ?? '-'}</td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

此时,在 App.tsx 中直接引用 DataTable 组件并传入 data 数组,就可以看到表格动态生成如下。

// 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} />
  );
}

image.png

接下来,就是加上无限滚动的效果。

通常来说,可滚动表格只会有一个表头,也就是只有一个 thead 部分,并且此处我们把它固定在顶部。再按照上述文字滚动的实现思路,我们只要在一个 table 中准备两份相同的 tbody 内容,然后给这两份 tbody 设置相同的动画即可。

同时,为了限制整体容器的高度,以防用户在表格位置滚动时感知到突变,造成不好的视觉体验,还需要给 table 加一层外部容器。

到此,整个分析过程就结束了,DataTable 组件的最终实现如下:

// DataTable.tsx
export default function DataTable<T extends object>({ dataSource }: { dataSource: Array<T> }) {
  if (!Array.isArray(dataSource) || dataSource.length === 0) {
    return null; // 数据源不是数组或数组为空,停止操作
  }

  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;
  }
}

刷新页面,再看一下页面效果,这样就实现表格的无限滚动了。

动画.gif

最后

前端新手一枚,如果有什么不对的地方或者更好的建议,欢迎大家提出。

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React-virtualized是一个非常流行的React库,它可以帮助我们实现大数据量的虚拟滚动效果。下面是一个使用React-virtualized实现虚拟滚动表格的示例代码: 首先,我们需要安装React-virtualized库: ``` npm install react-virtualized --save ``` 然后,我们需要引入Table和Column组件: ``` import { Table, Column } from 'react-virtualized'; ``` 接下来,我们可以定义一个数据源,例如: ``` const list = [ { name: '张三', age: '18', address: '北京市海淀区' }, { name: '李四', age: '20', address: '北京市朝阳区' }, { name: '王五', age: '22', address: '北京市西城区' }, // ... // 这里可以添加更多的数据 ]; ``` 然后,我们可以定义一个Table组件,指定它的rowCount和rowGetter属性: ``` <Table rowCount={list.length} rowGetter={({ index }) => list[index]} > ``` 接下来,我们可以添加一些Column组件,定义每一列的属性: ``` <Column label="姓名" dataKey="name" width={100} /> <Column label="年龄" dataKey="age" width={100} /> <Column label="地址" dataKey="address" width={200} /> ``` 最后,我们需要在Table组件中添加一些属性,以启用虚拟滚动: ``` <Table rowCount={list.length} rowGetter={({ index }) => list[index]} headerHeight={20} rowHeight={30} width={600} height={400} > ``` 在上面的代码中,我们设置了headerHeight和rowHeight属性来指定表头和每一行的高度,width和height属性用于指定表格的宽度和高度。React-virtualized会自动根据这些属性来计算出需要渲染的行数,并且只渲染当前可见的行,以实现虚拟滚动的效果。 完整的代码示例: ``` import React, { Component } from 'react'; import { Table, Column } from 'react-virtualized'; const list = [ { name: '张三', age: '18', address: '北京市海淀区' }, { name: '李四', age: '20', address: '北京市朝阳区' }, { name: '王五', age: '22', address: '北京市西城区' }, // ... // 这里可以添加更多的数据 ]; class VirtualTable extends Component { render() { return ( <Table rowCount={list.length} rowGetter={({ index }) => list[index]} headerHeight={20} rowHeight={30} width={600} height={400} > <Column label="姓名" dataKey="name" width={100} /> <Column label="年龄" dataKey="age" width={100} /> <Column label="地址" dataKey="address" width={200} /> </Table> ); } } export default VirtualTable; ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值