nex_music/components/LyricsPanel.tsx

70 lines
2.1 KiB
TypeScript

import React, { useEffect, useRef } from 'react';
import { LyricLine } from '../types';
interface LyricsPanelProps {
lyrics: LyricLine[];
currentTime: number;
}
const LyricsPanel: React.FC<LyricsPanelProps> = ({ lyrics, currentTime }) => {
const containerRef = useRef<HTMLDivElement>(null);
// Find the index of the last line that has started
const activeIndex = lyrics.reduce((acc, line, index) => {
if (line.time <= currentTime) {
return index;
}
return acc;
}, -1);
useEffect(() => {
if (activeIndex !== -1 && containerRef.current) {
const activeElement = containerRef.current.children[activeIndex] as HTMLElement;
if (activeElement) {
// Calculate scroll position to center the active element
// Using manual calculation is often smoother/more predictable than scrollIntoView for this specific "huge padding" case
const containerHeight = containerRef.current.clientHeight;
const elementTop = activeElement.offsetTop;
const elementHeight = activeElement.clientHeight;
containerRef.current.scrollTo({
top: elementTop - containerHeight / 2 + elementHeight / 2,
behavior: 'smooth'
});
}
}
}, [activeIndex]);
if (lyrics.length === 0) {
return (
<div className="flex items-center justify-center h-full text-neutral-500 italic">
<p>No lyrics available for this track.</p>
</div>
);
}
return (
<div
ref={containerRef}
className="h-full w-full overflow-y-auto no-scrollbar py-[15vh] md:py-[45vh] text-center px-4 relative"
>
{lyrics.map((line, index) => {
const isActive = index === activeIndex;
return (
<div
key={index}
className={`transition-all duration-500 ease-out py-3 origin-center ${
isActive
? 'text-amber-400 text-2xl md:text-3xl font-bold opacity-100 scale-105'
: 'text-neutral-400 text-lg md:text-xl font-medium opacity-40 blur-[0.5px]'
}`}
>
{line.text}
</div>
);
})}
</div>
);
};
export default LyricsPanel;