需求
页面上有个小喇叭,循环展示消息内容
逻辑思路
- 设置定时器,修改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
- 判断了内容不超出,就不滚动
- 如果内容超出了,但是内容太少,导致没有及时的handleMessage 没有处理这一块的逻辑。解决办法,就是double一下数据