react无限滚动hooks,一般用于大屏里的列表

356 阅读2分钟

useBigScreenScroll

原理

每隔2s,列表向上移动(利用css3 transition)一个item的高度,动画完毕后,将第一个item的数据,移到列表的最后面。

使用

import useBigScreenScroll from './useBigScreenScroll'

const rawData = [
  {
    user: '王*',
    phone: '159****0121',
    depart: '松北',
    orgName: '移动',
    time: '',
  },
  {
    user: '张*',
    phone: '136****9980',
    depart: '保定阜平综合驻点',
    orgName: '铁塔',
    time: '',
  },
  {
    user: '莫**',
    phone: '159****0990',
    depart: '联通业务维护',
    orgName: '联通',
    time: '',
  },
  {
    user: '李*',
    phone: '138****4988',
    depart: '铁岭市区驻点',
    orgName: '电信',
    time: '',
  },
]


const data1 = [...rawData, ...rawData]


const Index: React.FC = () => {
  const [data] = useState(() => {
    return data1.map((item, index) => {
      const time = moment(
        new Date().getTime() -
          1000 * 60 * 60 * 1.5 + // 一小时前
          (-1 + Math.random() * 2) * 1000 * 60 * 60 * 0.7 // 0.7小时内
      ).format('HH:mm:ss')
      return {
        ...item,
        id: index,
        time,
      }
    })
  })


  const scrollResult = useBigScreenScroll({
    data,
    itemHeight: '46px',
  })


  return (
    <div className={styles.container}>
      <Item user="工程师" phone="号码" depart="所属组织" orgName="运营商" time="接单时间" />
      <div className={styles.table_content}>
        <div className={styles.item_wrap} style={scrollResult.divStyle}>
          {scrollResult.scrollData.map((item, index) => {
            return <Item key={item.id} {...item} />
          })}
        </div>
      </div>
    </div>
  )
}
export default Index

效果

chrome-capture-2023-8-25.gif

代码

import { useInterval } from 'ahooks'
import { CSSProperties, useEffect, useState } from 'react'

interface IUseBigScreenScrollOptions {
  data?: any[]
  itemHeight: number | string
  transitionDuration?: number // 动画时长 ms
  scrollInterval?: number // 滚动间隔 ms
  judge?: () => boolean // 是否开启滚动 一般是判断当前数据的数量,如果数据量小于一屏就不滚动了
}

/**
 * @description 一般是大屏用的,table无限滚动的hooks
 * 注:scrollInterval要大于transitionDuration
 */
export default function useBigScreenScroll(options: IUseBigScreenScrollOptions): {
  scrollData: any[]
  divStyle: CSSProperties // 给list外层div设置的样式
} {
  const { data = [], itemHeight, transitionDuration = 500, scrollInterval = 2000, judge } = options

  const [data1, setData1] = useState<any[]>(data) // data1 是hooks内部保存的data
  const [divStyle, setDivStyle] = useState<CSSProperties>({})

  // 静止状态
  const style1: CSSProperties = {
    transition: 'none',
    transform: 'translateY(0px)',
  }

  // 滚动状态
  const style2: CSSProperties = {
    transition: `all ${(transitionDuration * 100) / 1000 / 100}s`,
    transform: `translateY(-${itemHeight})`,
  }

  useEffect(() => {
    if (data.length > 0) {
      setData1(data)
    }
  }, [JSON.stringify(data)])

  useInterval(() => {
    if (
      data.length > 0 && //
      (!judge || (judge && judge()))
    ) {
      setDivStyle(style2)
      setTimeout(() => {
        setDivStyle(style1)

        // 把第一个元素放到最后
        const nextData1 = [...data1]
        const first = {
          ...nextData1[0],
        }
        nextData1.shift()
        nextData1.push(first)
        setData1(nextData1)
      }, transitionDuration + 6)
    }
  }, scrollInterval)

  return {
    scrollData: data1,
    divStyle,
  }
}