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一下数据
相关推荐
Anson Jiang7 分钟前
浏览器标签页管理:使用chrome.tabs API实现新建、切换、抓取内容——Chrome插件开发从入门到精通系列教程06
开发语言·前端·javascript·chrome·ecmascript·chrome devtools·chrome插件
掘金安东尼10 分钟前
黑客劫持:周下载量超20+亿的NPM包被攻击
前端·javascript·面试
剑亦未配妥1 小时前
移动端触摸事件与鼠标事件的触发机制详解
前端·javascript
前端君9 小时前
实现最大异步并发执行队列
javascript
知识分享小能手10 小时前
React学习教程,从入门到精通,React 组件核心语法知识点详解(类组件体系)(19)
前端·javascript·vue.js·学习·react.js·react·anti-design-vue
蚂蚁RichLab前端团队11 小时前
🚀🚀🚀 RichLab - 花呗前端团队招贤纳士 - 【转岗/内推/社招】
前端·javascript·人工智能
萌萌哒草头将军11 小时前
Oxc 和 Rolldown Q4 更新计划速览!🚀🚀🚀
javascript·vue.js·vite
Qlittleboy12 小时前
uniapp如何使用本身的字体图标
javascript·vue.js·uni-app
林_深时见鹿12 小时前
Vue + ElementPlus 自定义指令控制输入框只可以输入数字
前端·javascript·vue.js
GDAL12 小时前
Knockout.js 任务调度模块详解
javascript·knockout