使用React+ant Table 实现 表格无限循环滚动播放

发布于:2025-06-07 ⋅ 阅读:(22) ⋅ 点赞:(0)

数据大屏表格数据,当表格内容超出(出现滚动条)时,无限循环滚动播放,鼠标移入暂停滚动,鼠标移除继续滚动;数据量小没有超出时不需要滚动。
*使用时应注意,滚动区域高度=父元素高度 - 表头高度
1、组件内容

import React, { useState, useEffect, useRef } from "react";
import { Table } from "antd";
import { ColumnsType, TableRef } from "antd/lib/table";
import styles from "./styles.less";
import Nodate from "../Other/nodata";

interface InfiniteScrollTableProps<T> {
  /** 表格数据源 */
  dataSource: T[];
  /** 表格列定义 */
  columns: ColumnsType<T>;
  /**
   * 唯一字段
   */
  rowKeyField: string;
  /**
   * 滚动速率。
   * @default 0.5
   * @description 建议在 0.5-3 之间调整
   * */
  speed?: number;
}

/**
 * @description 无限循环滚动table
 */
const InfiniteScrollTable = <T = any,>(props: InfiniteScrollTableProps<T>) => {
  const { dataSource, columns, speed = 0.5, rowKeyField = "key" } = props;
  const [doubleData, setDoubleData] = useState<any[]>([]);
  const tableRef = useRef<TableRef>(null);
  const animationRef = useRef<number | null>(null);
  const isHovered = useRef(false);
  // 滚动高度
  const scrollHeight = useRef(0);

  // 滚动动画
  const startScrolling = (begin: boolean) => {
    if (isHovered.current || !tableRef.current || !tableHasScroll()) return;

    const table = tableRef.current.nativeElement;
    const wrapper = table.querySelector(".ant-table-body");
    if (!wrapper) {
      return;
    }
    // 重置滚动位置
    if (begin) {
      wrapper.scrollTop = 0;
    }

    const scroll = () => {
      if (isHovered.current) return;

      // 滚动到底部时重置位置
      if (wrapper.scrollTop >= wrapper.scrollHeight / 2) {
        wrapper.scrollTop = 0;
      } else {
        wrapper.scrollTop += speed;
      }

      animationRef.current = requestAnimationFrame(scroll);
    };

    animationRef.current = requestAnimationFrame(scroll);
  };

  // 表格内容是否出现滚动
  const tableHasScroll = () => {
    const table = tableRef.current?.nativeElement;
    const wrapper = table?.querySelector(".ant-table-body");
    if (!wrapper) {
      return false;
    }
    const hasScroll = wrapper.scrollHeight > wrapper.clientHeight;
    return hasScroll;
  };

  // 停止滚动
  const stopScrolling = () => {
    if (animationRef.current) {
      cancelAnimationFrame(animationRef.current);
      animationRef.current = null;
    }
  };

  // 处理鼠标事件
  const handleMouseEnter = () => {
    isHovered.current = true;
    stopScrolling();
  };

  const handleMouseLeave = () => {
    isHovered.current = false;
    startScrolling(false);
  };

  useEffect(() => {
    // 先设置为初始数据
    setDoubleData([...dataSource]);
  }, [dataSource]);

  // 开始滚动
  useEffect(() => {
    // 创建两倍数据用于实现无缝滚动
    if (tableHasScroll() && doubleData.length === dataSource.length) {
      setDoubleData([...dataSource, ...dataSource]);
    }
    startScrolling(true);
    return () => stopScrolling();
  }, [tableRef.current, doubleData]);

  return (
    <div
      ref={(el) => (scrollHeight.current = el?.clientHeight || 0)}
      className={styles["infinite-scroll-table"]}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
    >
      <Table
        ref={tableRef}
        columns={columns}
        dataSource={doubleData}
        pagination={false}
        scroll={{ y: scrollHeight.current - 57 }}
        rowClassName={(record, index) =>
          index % 2 === 0 ? styles["even-row"] : styles["odd-row"]
        }
        rowKey={(record: any, index) => (record?.[rowKeyField] ?? "") + index}
      />
    </div>
  );
};

export default InfiniteScrollTable;

2、样式



.infinite-scroll-table {
    position: relative;
    height: 100%;
    transition: all 0.3s ease;
    border: 1px solid rgba(187,187,187,1);

    .highlight {
        color: #40a9ff;
        font-weight: 600;
    }

    .even-row {
        background: rgba(255,255,255);
        height: 60px;
    }
    
    .odd-row {
        background: rgba(250,250,250);
        height: 60px;
    }

    :global {
        .ant-table-header{
            border-radius: 0;
        }
        
        .ant-table-thead > tr > th {
            background: rgba(242,242,242) !important;
            color: #333 !important;
            font-size: 14px;
            font-weight: 600;
            text-align: center;
            border-start-start-radius: 0 !important;
            border-start-end-radius: 0 !important;
        }
        
        .ant-table-body {
            scrollbar-width: none;
            -ms-overflow-style: none;
        }
        
        .ant-table-cell{
            font-weight: normal;
            font-size: 14px;
        }
        
        .ant-table-body::-webkit-scrollbar {
            display: none;
        }
        
        .ant-table-row:hover > td {
            background: rgba(64, 144, 255, 0.2) !important;
        }

        .ant-table-placeholder .ant-table-cell{
            border: none;
        }

    }
    
}


网站公告

今日签到

点亮在社区的每一天
去签到