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一下数据
相关推荐
Eloudy1 小时前
ReAct 原理简介
前端·javascript·人工智能·react.js·agent·gpu
ZJU_统一阿萨姆2 小时前
【DSH】如何将 DeepSeek Harness 嵌入生产环境?万字解构 DeepSeek Agent Harness 的运行机制与安全边界
前端·javascript·安全
嘟嘟07173 小时前
Next.js 博客左侧笔记列表是怎么从 Redis 渲染出来的?从组件、children 到动态路由一次讲清
redis·react.js·前端工程化
sunly_4 小时前
TypeScript总结:16、面向对象速查
前端·javascript·typescript
张元清4 小时前
React useInterval Hook:没有过期闭包的 setInterval (2026)
javascript·react.js
MXN_小南学前端4 小时前
React超长文本域中实现“返回顶部”浮动按钮
前端·javascript·react.js
gs801404 小时前
解构 Cordis:面向“时空可组合性”的 TypeScript 元框架深度剖析
前端·javascript·typescript
Highcharts.js5 小时前
Highcharts大数据渲染模块Boost实战与参数最佳实践表
javascript·数据可视化·boost·highcharts·大数据渲染·加速配置·参数表
岁岁种桃花儿5 小时前
Vue核心语法第一篇:Vue是什么?
前端·javascript·vue.js
Highcharts.js5 小时前
数据可视化避坑指南 ——开发中常见图表错误与修复方案
javascript·信息可视化·数据可视化·highcharts·数据可视化避坑·修复方案