React 消息文本循环展示

需求

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

逻辑思路

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

实现效果

代码

typescript 复制代码
/*
 * @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;
css 复制代码
.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一下数据
相关推荐
PYB315 小时前
【Web·JS·基础】函数的使用和展运算符...objs
前端·javascript
默_笙16 小时前
🚥 给 RAG 装一台心电监护仪:LangSmith 全链路观测(上)
前端·javascript
变与不变80618 小时前
js同步和异步难点重点详解
开发语言·javascript·ecmascript
Dovis(誓平步青云)18 小时前
多个链接不等于多份证据,新闻核验看板怎样合并来源
java·服务器·前端·javascript·人工智能·pdf·电脑
Dovis(誓平步青云)19 小时前
几个方案来回选不定?做一个随时切换的候选推荐页
android·java·服务器·开发语言·javascript·数据库·智能化
可乐鸡翅yeah_20 小时前
fmp4 碎片 MP4 格式 HLS 流,和传统 TS 分片有什么不一样
开发语言·javascript·ios·音视频·safari·m3u8·m3u8在线播放
ss27321 小时前
AI全栈实战 | 2.3-01 组件库实战:Element Plus 怎么搭出专业界面,过度封装为什么成维护负担
前端·javascript·elementui·vue
福兮说21 小时前
URL 编码的七个坑:c++ 传到后端变空格、%25 套娃、截断 emoji 直接报错
开发语言·前端·javascript·node.js·url
小的时候可菜了1 天前
JS 异步的本质:从单线程到 thenable(一条进化链)
开发语言·前端·javascript
可乐鸡翅yeah_1 天前
hls.js 配置 loadStartPosition,点播起始播放位置踩坑
开发语言·javascript·网络协议·ecmascript·m3u8在线