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