parent
9d1102c1b3
commit
f0c00aa830
@ -0,0 +1,90 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 滚动容器
|
||||
* TODO: 自定义滚动速度
|
||||
*/
|
||||
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
|
||||
interface TextScrollProps {
|
||||
hover?: boolean;
|
||||
speed?: number;
|
||||
width: string;
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const TextScroll: React.FC<TextScrollProps> = ({
|
||||
width = '100px',
|
||||
className = '',
|
||||
children = '',
|
||||
hover = false,
|
||||
speed = 30,
|
||||
}) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const childrenRef = useRef<HTMLParagraphElement>(null);
|
||||
const [isOverflowing, setisOverflowing] = useState<boolean>(false); // 子元素宽度是否溢出
|
||||
const [isHovered, setIsHovered] = useState<boolean>(false); // 鼠标是否在 hover 状态
|
||||
const [animation, setAnimation] = useState<string>(''); // 滚动动画
|
||||
|
||||
const handleMouseMove = (isMouseInside: boolean) => {
|
||||
if (!hover) return; // 未传入 'hover = true' 时不响应鼠标事件
|
||||
if (!childrenRef.current) return; // 获取不到 children 时不响应
|
||||
if (isMouseInside && isHovered) return; // 鼠标已经进入时不响应
|
||||
|
||||
if (isMouseInside) {
|
||||
childrenRef.current.style.animation = animation;
|
||||
} else {
|
||||
childrenRef.current.style.animation = '';
|
||||
childrenRef.current.style.transform = 'translateX(0)';
|
||||
}
|
||||
|
||||
setIsHovered(isMouseInside);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const containerWidth = containerRef.current?.offsetWidth;
|
||||
const childrenWidth = childrenRef.current?.offsetWidth;
|
||||
|
||||
if (containerWidth && childrenWidth && containerWidth < childrenWidth) {
|
||||
setisOverflowing(true);
|
||||
setAnimation(`scroll ${childrenWidth / speed}s linear infinite`);
|
||||
if (!hover) childrenRef.current.style.animation = animation;
|
||||
}
|
||||
}, [children, speed, hover, animation]); // 当children变化时重新计算
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={`relative h-auto overflow-hidden whitespace-nowrap`}
|
||||
style={{ width }}
|
||||
onMouseEnter={() => handleMouseMove(true)}
|
||||
onMouseLeave={() => handleMouseMove(false)}
|
||||
>
|
||||
<div ref={childrenRef} className={`flex flex-row items-center relative w-[fit-content] h-auto ${className}`}>
|
||||
{children}
|
||||
{isOverflowing && (
|
||||
<>
|
||||
<div> </div>
|
||||
{children}
|
||||
<div> </div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<style jsx>{`
|
||||
@keyframes scroll {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextScroll;
|
Loading…
Reference in new issue