From 0560114d981796c7996a77a3025292078acf694f Mon Sep 17 00:00:00 2001 From: angel Date: Thu, 19 Jun 2025 20:47:01 +0000 Subject: [PATCH] feat: enhance FullScreenPlayer with improved lyrics scrolling and toggle functionality --- app/components/FullScreenPlayer.tsx | 165 +++++++++++++++++----------- 1 file changed, 102 insertions(+), 63 deletions(-) diff --git a/app/components/FullScreenPlayer.tsx b/app/components/FullScreenPlayer.tsx index 6fea490..4a97a96 100644 --- a/app/components/FullScreenPlayer.tsx +++ b/app/components/FullScreenPlayer.tsx @@ -14,7 +14,8 @@ import { FaVolumeXmark, FaShuffle, FaRepeat, - FaXmark + FaXmark, + FaQuoteLeft } from "react-icons/fa6"; import { Card, CardContent } from '@/components/ui/card'; import { ScrollArea } from '@/components/ui/scroll-area'; @@ -80,32 +81,73 @@ export const FullScreenPlayer: React.FC = ({ isOpen, onCl setCurrentLyricIndex(newIndex); }, [lyrics, currentTime]); - // Auto-scroll lyrics to center current line + // Auto-scroll lyrics to center current line without cutting off text useEffect(() => { - if (currentLyricIndex >= 0 && lyrics.length > 0) { - const lyricsContainer = document.querySelector('.lyrics-container'); - if (lyricsContainer) { - const currentLyricElement = lyricsContainer.children[currentLyricIndex] as HTMLElement; - if (currentLyricElement) { - currentLyricElement.scrollIntoView({ - behavior: 'smooth', - block: 'center', - inline: 'nearest' - }); + if (currentLyricIndex >= 0 && lyrics.length > 0 && showLyrics) { + // Use a small delay to ensure the DOM is updated + const scrollTimeout = setTimeout(() => { + const lyricsScrollArea = document.querySelector('[data-radix-scroll-area-viewport]'); + if (lyricsScrollArea) { + const currentLyricElement = lyricsScrollArea.querySelector(`[data-lyric-index="${currentLyricIndex}"]`) as HTMLElement; + if (currentLyricElement) { + const containerHeight = lyricsScrollArea.clientHeight; + const elementHeight = currentLyricElement.offsetHeight; + const elementOffsetTop = currentLyricElement.offsetTop; + + // Calculate scroll position to center the current lyric + const targetScrollTop = elementOffsetTop - (containerHeight / 2) + (elementHeight / 2); + + lyricsScrollArea.scrollTo({ + top: Math.max(0, targetScrollTop), + behavior: 'smooth' + }); + } } - } + }, 50); + + return () => clearTimeout(scrollTimeout); } - }, [currentLyricIndex, lyrics.length]); + }, [currentLyricIndex, lyrics.length, showLyrics]); + + // Reset lyrics to top when song ends or changes + useEffect(() => { + if (currentTrack && showLyrics) { + const lyricsScrollArea = document.querySelector('[data-radix-scroll-area-viewport]'); + if (lyricsScrollArea) { + lyricsScrollArea.scrollTo({ + top: 0, + behavior: 'smooth' + }); + } + setCurrentLyricIndex(-1); + } + }, [currentTrack, showLyrics]); // Sync with main audio player useEffect(() => { const syncWithMainPlayer = () => { const mainAudio = document.querySelector('audio') as HTMLAudioElement; if (mainAudio && currentTrack) { - setCurrentTime(mainAudio.currentTime); - setDuration(mainAudio.duration || 0); - setProgress(mainAudio.duration ? (mainAudio.currentTime / mainAudio.duration) * 100 : 0); - setIsPlaying(!mainAudio.paused); + const newCurrentTime = mainAudio.currentTime; + const newDuration = mainAudio.duration || 0; + const newIsPlaying = !mainAudio.paused; + + // Check if song ended (reset lyrics to top) + if (newCurrentTime === 0 && !newIsPlaying && currentTime > 0) { + const lyricsScrollArea = document.querySelector('[data-radix-scroll-area-viewport]'); + if (lyricsScrollArea) { + lyricsScrollArea.scrollTo({ + top: 0, + behavior: 'smooth' + }); + } + setCurrentLyricIndex(-1); + } + + setCurrentTime(newCurrentTime); + setDuration(newDuration); + setProgress(newDuration ? (newCurrentTime / newDuration) * 100 : 0); + setIsPlaying(newIsPlaying); setVolume(mainAudio.volume); } }; @@ -118,7 +160,7 @@ export const FullScreenPlayer: React.FC = ({ isOpen, onCl const interval = setInterval(syncWithMainPlayer, 100); return () => clearInterval(interval); } - }, [isOpen, currentTrack]); + }, [isOpen, currentTrack, currentTime]); // Extract dominant color from cover art useEffect(() => { @@ -286,7 +328,7 @@ export const FullScreenPlayer: React.FC = ({ isOpen, onCl - {/* Volume */} + {/* Volume and Lyrics Toggle */}
+ {lyrics.length > 0 && ( + + )} + {showVolumeSlider && (
= ({ isOpen, onCl {/* Right Side - Lyrics */} {showLyrics && lyrics.length > 0 && (
- - -
-

Lyrics

- +
+ +
+ {lyrics.map((line, index) => ( +
+ {line.text || '♪'} +
+ ))} + {/* Add extra padding at the bottom to allow last lyric to center */} +
- - -
- {lyrics.map((line, index) => ( -
- {line.text || '♪'} -
- ))} -
-
- - -
- )} - - {/* Show Lyrics button when hidden */} - {!showLyrics && lyrics.length > 0 && ( -
- + +
)}