记录一个不错的数字翻牌器

18 阅读2分钟
js

import './index.less';
import { useEffect, useState } from 'react';
import CountUp from 'react-countup';
import { autoplaySpeed } from '@/global';

//此数据关联滚动次数刷新机制
const numArr = [
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5,
  6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1,
  2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,
  8, 9,
];

interface Props {
  num: string | number; // '123' | 123
  refreshTime?: number; // 刷新时间 默认10秒
}
const Digit = (props: Props) => {
  // 定时器
  let intervalId: any = null;
  let updateTimes = 0; // 刷新次数 超过五次还原初始位置

  const { num, refreshTime } = props; //传入的数字和刷新时间

  const [digits, setDigits] = useState(numArr); // 滚动的数字数组
  const [numberStr, setNumberStr] = useState(); // 传入的数字
  const [refArr, setRefArr] = useState([]); // 滚动的dom数组
  const [firstPointArr, setFirstPointArr] = useState([]); // 第一次的位置数据
  const [countState, setCountState] = useState(0); // 0 countUp 1 数字滚动刷新

  //判断num是否为数字
  function isNumber(num: any) {
    return (
      (typeof num === 'number' || typeof Number(num) === 'number') &&
      !isNaN(num)
    );
  }

  //开启定时任务
  function startIntervalTask() {
    if (!intervalId && isNumber(num)) {
      const intervalId = setInterval(
        () => {
          //记录刷新次数
          updateTimes++;
          refArr.forEach((el, index) => {
            if (el) {
              //获取translate 值
              let translateY = el.style.transform.split(',')[1]?.split(')')[0];
              //去掉px
              translateY = Number(translateY?.slice(0, -2));
              el.style.transform = `translate(-50%,${translateY - 320}px)`;
            }
          });
          // 刷新次数超过5次 还原位置
          if (updateTimes > 5) {
            updateTimes = 0;
            refArr.forEach((el, index) => {
              if (el) {
                el.style.transform = `translate(-50%,${firstPointArr[index]}px)`;
              }
            });
            return;
          }
        },
        refreshTime ? refreshTime : autoplaySpeed,
      );
      return intervalId;
    } else {
      //console.log("服务已经启动")
    }
  }

  // 监听num变化
  useEffect(() => {
    if (isNumber(num)) {
      setCountState(0); //num变化 改为countUp组件重新初始化
      setNumberStr(num);
      setRefArr(new Array(num?.toString().length).fill(null));
    }
  }, [num]);

  useEffect(() => {
    setTimeout(() => {
      //2秒后改为自动轮播逻辑
      if (countState == 0) {
        setCountState(1);
      }
    }, 2000);
  }, [refArr]);

  //监听countState变化
  useEffect(() => {
    if (countState == 1) {
      //记录第一次的位置数据
      const pointArr: any = [];
      refArr.forEach((el, index) => {
        if (el) {
          //获取translate值
          let translateY = el.style.transform.split(',')[1]?.split(')')[0];
          //去掉px
          translateY = Number(translateY?.slice(0, -2));
          pointArr.push(translateY);
        }
      });
      setFirstPointArr(pointArr);
    }
  }, [countState]);

  //监听pointArr 开启定时任务
  useEffect(() => {
    let id: any = null;
    if (firstPointArr.length > 0) {
      id = startIntervalTask();
    }
    return () => {
      clearInterval(id);
      id = null;
    };
  }, [firstPointArr]);

  return (
    <div className="numberDsiplay">
      {numberStr
        ?.toString()
        .split('')
        .map((digitStr, index) =>
          countState == 0 ? (
            <div
              className="digitWrapper"
              key={'CountUp-' + digitStr + '-' + index}
            >
              <CountUp
                start={0}
                separator=""
                duration={2}
                end={Number(digitStr)}
              />
            </div>
          ) : (
            <div
              className="digitWrapper"
              key={'digit-' + digitStr + '-' + index}
            >
              <span
                className="digitList"
                ref={(el) => {
                  refArr[index] = el;
                  setRefArr(refArr);
                }}
                style={{
                  transform: `translate(-50%,${-Number(digitStr) * 32}px)`,
                  transitionDelay: `${index * 100}ms`,
                }}
              >
                {digits.map((digit, index) => (
                  <span
                    className="digit"
                    key={
                      'digit-' +
                      digit +
                      '-' +
                      index +
                      '-' +
                      (digitStr + '-' + index)
                    }
                  >
                    {digit}
                  </span>
                ))}
              </span>
            </div>
          ),
        )}
    </div>
  );
};

export default Digit;


css

.numberDsiplay {
  display: flex;
  align-items: center;
  justify-content: center;
  // padding: 5px 0;
  .digitWrapper {
    width: 24px;
    text-align: center;
    height: 32px;
    font-size: 24px;
    line-height: 32px;
    // background-color: black;
    border: 1px transparent solid;
    border-image: linear-gradient(to bottom, #00ffff, #29292c) 2 10;
    position: relative;
    // border-radius: 6px;
    // margin-right: 0;
    overflow: hidden;
    span {
      color: #f0ff00 !important;
    }
    &:last-child {
      margin-right: 5px;
    }
  }

  .digitList {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    color: #f0ff00 !important;
    display: flex;
    flex-direction: column;
    align-items: center;
    transition: transform 600ms;
  }
}

效果

image.png

定时滚动