shuangmianxiaoQ / study-note

日常学习或工作笔记
6 stars 1 forks source link

列表无限循环滚动(中奖名单滚动) #61

Open shuangmianxiaoQ opened 3 years ago

shuangmianxiaoQ commented 3 years ago

看似简单的需求,但也踩了不少坑,一步一步来总能解决问题!

实现原理

  1. 将数组第一项加到数组尾部(push)
  2. 利用css动画将列表容器上移
  3. 将数组第一项删除(shift)
  4. 列表容器恢复初始态

核心代码

import React, { useEffect, useRef, useState } from 'react';
import cx from 'classnames';
import { requestInterval, clearRequestInterval } from '@essentials/request-interval';
import { px2vw } from '@/utils/helper';
import styles from './index.module.scss';

const list = [
  {
    id: 1,
    picUrl: 'https://imagev2.xmcdn.com/group44/M02/69/AB/wKgKkVsRFeHSZ9NvAABqVg24cIc530.jpg',
    desc: '用户xxx获得专辑《德云社》',
  },
  {
    id: 2,
    picUrl: 'https://imagev2.xmcdn.com/group44/M02/69/AB/wKgKkVsRFeHSZ9NvAABqVg24cIc530.jpg',
    desc: '用户xxx获得专辑《三体全集》',
  },
  {
    id: 3,
    picUrl: 'https://imagev2.xmcdn.com/group44/M02/69/AB/wKgKkVsRFeHSZ9NvAABqVg24cIc530.jpg',
    desc: '用户xxx获得专辑《德云社》',
  },
  {
    id: 4,
    picUrl: 'https://imagev2.xmcdn.com/group44/M02/69/AB/wKgKkVsRFeHSZ9NvAABqVg24cIc530.jpg',
    desc: '用户xxx获得专辑《三体全集》',
  },
];

const Footer = () => {
  const [winnerList, setWinnerList] = useState<any[]>([]);
  const [animate, setAnimate] = useState(false);
  const listRef = useRef<any[]>([]);
  const rafInterval = useRef<any>(null);

  useEffect(() => {
    setWinnerList(list);
    listRef.current = list;
    if (list.length >= 3) {
      rafInterval.current = requestInterval(scrollUp, 1500);
    }

    return () => {
      clearRequestInterval(rafInterval.current);
    };
  }, []);

  const scrollUp = () => {
    listRef.current.push(listRef.current[0]);
    setWinnerList(listRef.current);
    setAnimate(true);

    setTimeout(() => {
      listRef.current.shift();
      setWinnerList(listRef.current);
      setAnimate(false);
    }, 1200);
  };

  return (
    <div className={styles.listWrapper}>
      <div
        className={cx(styles.list, { [styles.animate]: animate })}
        style={{ transform: animate ? `translateY(-${px2vw(55)}vw)` : 'none' }}
      >
        {winnerList.map(({ picUrl, desc }, index) => (
          <div className={styles.item} key={index}>
            <img src={picUrl} alt="" className={styles.avatar} />
            <div className={styles.desc}>{desc}</div>
          </div>
        ))}
      </div>
    </div>
  );
};
.listWrapper {
  height: 150px;
  overflow: hidden;
}

.animate {
  transition: all 0.3s linear;
}

遇到的问题及解决方法

  1. 使用transformtranslateY替换marginTop:防止重绘导致动画不流畅
  2. setInterval的时间间隔要稍大于setTimeout,防止还未删除又加入新的数据导致重复
  3. 使用setInterval依然有数据错乱的问题,排查后发现是切换Tab时出现,原来是浏览器离开当前窗口失焦时setInterval并不能如愿的继续定时执行任务,时间误差导致了数据增多而错乱。针对该问题一开始使用setTimeout代替setInterval,但并不能解决问题。然后使用requestAnimationFrame替换setInterval,问题得到了解决
  4. 项目是移动端的,一开始是在onTransitionEnd回调中删除数据,发现在移动端息屏后不滚动了,排查后发现数组中数据没被删除,自然是transition未执行导致的,换回setTimeout则完美解决问题