React 消息文本循环展示

需求

页面上有个小喇叭,循环展示消息内容

逻辑思路

  • 设置定时器,修改translateX属性来实现滚动,
  • 判断滚动位置,修改list位置来实现无限滚动

实现效果

代码

/*
 * @Author: Do not edit
 * @Date: 2023-09-07 11:11:45
 * @LastEditors: atwlee
 * @LastEditTime: 2023-09-07 15:23:21
 * @Description:
 * @FilePath: /pan-ui/packages/Base/src/MessageScroll/index.tsx
 */

import { ReactNode, forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
import './index.css';

export interface MessageScrollProps {
  messages: ReactNode[];
  speed?: number;
  gap?: number;
}

export interface MessageScrollRef {
  start: () => void;
  pause: () => void;
  restart: (sleep?: number) => void;
}

const Index = forwardRef<MessageScrollRef, MessageScrollProps>((props, ref) => {
  const { messages, speed = 20, gap = 20 } = props;
  const [messageList, setMessageList] = useState<ReactNode[]>([]);
  const messageListRef = useRef<ReactNode[]>([]);
  const [translateX, setTranslateX] = useState(0);
  const container = useRef<HTMLDivElement>(null);
  const exceed = useRef(false);
  const scrollX = useRef(0);
  const run = useRef(true);

  useEffect(() => {
    setMessageList(messages);
  }, [messages]);

  useEffect(() => {
    restart(0);
    if (container.current) {
      exceed.current = container.current.clientWidth < container.current.scrollWidth - gap;
    }
    messageListRef.current = messageList;
  }, [messageList]);

  const handleMessage = () => {
    const firstChildWidth = container.current?.firstElementChild?.clientWidth;
    if (firstChildWidth && scrollX.current >= firstChildWidth + gap) {
      const [first, ...rest] = messageListRef.current;
      setMessageList([...rest, first]);
    }
  };

  useEffect(() => {
    const timer = setInterval(() => {
      if (run.current && exceed.current) {
        scrollX.current += 0.5;
        handleMessage();
        setTranslateX(translateX - scrollX.current);
      }
    }, speed);
    return () => clearInterval(timer);
  }, []);

  const restart = (sleep = 200, reset = false) => {
    setTranslateX(0);
    reset && setMessageList(messages);
    scrollX.current = 0;
    run.current = false;
    const timer = setTimeout(() => {
      run.current = true;
      clearTimeout(timer);
    }, sleep);
  };
  useImperativeHandle(ref, () => ({
    start: () => {
      run.current = true;
    },
    pause: () => {
      run.current = false;
    },
    restart: (sleep) => {
      restart(sleep, true);
    },
  }));

  return (
    <div className="rc-message-scroll-container" ref={container}>
      {messageList.map((message, index) => (
        <div
          key={index}
          className="rc-message-scroll-item"
          style={{ transform: `translate(${translateX}px)`, marginRight: `${gap}px` }}
        >
          {message}
        </div>
      ))}
    </div>
  );
});

export default Index;

.rc-message-scroll-container {
    position: relative;
    display: flex;
    flex-wrap: nowrap;
    overflow: hidden;
}
.rc-message-scroll-container .rc-message-scroll-item{
    flex-shrink: 0;
}

FAQ

  1. 判断了内容不超出,就不滚动
  2. 如果内容超出了,但是内容太少,导致没有及时的handleMessage 没有处理这一块的逻辑。解决办法,就是double一下数据

你可能感兴趣的:(React技术栈相关,CSS,JS,react.js,javascript)