diff --git a/src/components/AudioPlayer/Player.tsx b/src/components/AudioPlayer/Player.tsx index 1a1247d..98db5ba 100644 --- a/src/components/AudioPlayer/Player.tsx +++ b/src/components/AudioPlayer/Player.tsx @@ -3,6 +3,8 @@ import { useState } from 'react'; import Image from 'next/image'; +import { AutoScrollContainer } from '@/components'; + export default function AudioPlayer({ className }: { className?: string }) { const [curTime, setCurTime] = useState('00:00'); const [totalTime, setTotalTime] = useState('00:00'); @@ -16,14 +18,12 @@ export default function AudioPlayer({ className }: { className?: string }) { {/* title & author */}
- {/* 改成滚动的 */} -

- {'Ferrum Aeternum'} -

- {/* 改成滚动的 */} -

- {'Ensiferum/mmmmsa'} -

+ +
+

{'Ferrum Aeternumaaaaaaaaaaa'}

+

{'Ensiferum/mmmmsa'}

+
+
{/* progress bar */} diff --git a/src/components/common/AutoScrollContainer.tsx b/src/components/common/AutoScrollContainer.tsx new file mode 100644 index 0000000..0d01241 --- /dev/null +++ b/src/components/common/AutoScrollContainer.tsx @@ -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 = ({ + width = '100px', + className = '', + children = '', + hover = false, + speed = 30, +}) => { + const containerRef = useRef(null); + const childrenRef = useRef(null); + const [isOverflowing, setisOverflowing] = useState(false); // 子元素宽度是否溢出 + const [isHovered, setIsHovered] = useState(false); // 鼠标是否在 hover 状态 + const [animation, setAnimation] = useState(''); // 滚动动画 + + 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 ( +
handleMouseMove(true)} + onMouseLeave={() => handleMouseMove(false)} + > +
+ {children} + {isOverflowing && ( + <> +
      
+ {children} +
      
+ + )} +
+ + +
+ ); +}; + +export default TextScroll; diff --git a/src/components/index.ts b/src/components/index.ts index 7b1519a..89f4075 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -19,6 +19,7 @@ export { default as HotJournalList } from './Journal/HotJournalList'; // Common export { default as Input } from './common/Input'; export { default as Button } from './common/Button'; +export { default as AutoScrollContainer } from './common/AutoScrollContainer'; // Audio Player export { default as PlayerBar } from './AudioPlayer/PlayerBar';