Skip to content

Commit

Permalink
🔧 chore: improve performance
Browse files Browse the repository at this point in the history
Signed-off-by: SimonShiki <[email protected]>
  • Loading branch information
SimonShiki committed Nov 30, 2024
1 parent 0b1fc14 commit 8f2f2b4
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 30 deletions.
85 changes: 58 additions & 27 deletions src/components/lyrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,81 @@ export default function Lyrics ({lyrics, className = ''}: LyricsProps) {
const progress = useAtomValue(progressJotai) * 1000;
const [prevHighlight, setPrevHighlight] = useState(0);
const [lastScrolled, setLastScrolled] = useState(-1001);
const virtuosoRef = useRef <VirtuosoHandle>(null);
const virtuosoRef = useRef<VirtuosoHandle>(null);
const scrollTimeoutRef = useRef<number>();
const isScrollingRef = useRef(false);

useEffect(() => {
if (!lyrics) return;
setParsedLyrics(parseLyrics(lyrics));
virtuosoRef.current?.scrollTo({ top: 0 });
}, [lyrics]);

const shouldHighlight = useCallback((index: number) => {
if (typeof parsedLyrics !== 'object') return false;
const next = parsedLyrics.lines[index + 1];
const line = parsedLyrics.lines[index];
return progress > line.time && progress < (next?.time ?? Infinity);
}, [parsedLyrics, progress]);

const scrollToIndex = useCallback((index: number) => {
if (isScrollingRef.current) return;

isScrollingRef.current = true;
window.cancelAnimationFrame(scrollTimeoutRef.current!);

scrollTimeoutRef.current = window.requestAnimationFrame(() => {
virtuosoRef.current?.scrollToIndex(Math.max(0, index - 1));
setLastScrolled(Date.now());

setTimeout(() => {
isScrollingRef.current = false;
}, 100);
});
}, []);

useEffect(() => {
if (typeof parsedLyrics !== 'object') return;

for (let i = 0; i < parsedLyrics.lines.length; i++) {
if (!shouldHighlight(i)) continue;
if (prevHighlight !== i && Date.now() - lastScrolled > 1000) {
virtuosoRef.current?.scrollToIndex(Math.max(0, i - 1));
scrollToIndex(i);
setPrevHighlight(i);
}
break;
}
}, [progress]);
}, [progress, parsedLyrics, prevHighlight, lastScrolled, shouldHighlight, scrollToIndex]);

const shouldHighlight = useCallback((index: number) => {
if (typeof parsedLyrics !== 'object') return false;
const next = parsedLyrics.lines[index + 1];
const handleLineClick = useCallback((time: number) => {
player.setProgress(time / 1000);
}, []);

const renderItem = useCallback((index: number) => {
if (typeof parsedLyrics !== 'object') return null;
const line = parsedLyrics.lines[index];
return progress > line.time && progress < (next?.time ?? Infinity);
}, [parsedLyrics, progress]);
const highlight = shouldHighlight(index);

return (
<div
className='flex flex-col my-2 *:text-pretty bg-white bg-op-0 hover:bg-op-20 transition-colors p-2 rounded-md'
onClick={() => handleLineClick(line.time)}
>
<span className={`color-white transition-all font-size-xl lg:font-size-2xl font-bold ${
highlight ? 'opacity-90 font-size-2xl lg:font-size-3xl' : 'blur-1 opacity-20'
}`}>
{line.content}
</span>
{line.translation && (
<span className={`color-white transition-all font-size-sm font-600 ${
highlight ? 'opacity-90 lg:font-size-lg' : 'blur-1 opacity-20'
}`}>
{line.translation}
</span>
)}
</div>
);
}, [parsedLyrics, shouldHighlight]);

if (typeof parsedLyrics === 'string') {
return (
Expand All @@ -56,25 +105,7 @@ export default function Lyrics ({lyrics, className = ''}: LyricsProps) {
totalCount={parsedLyrics.lines.length}
ref={virtuosoRef}
onScroll={() => setLastScrolled(Date.now())}
itemContent={(index) => {
const line = parsedLyrics.lines[index];
const highlight = shouldHighlight(index);

return (
<div className='flex flex-col my-2 *:text-pretty bg-white bg-op-0 hover:bg-op-20 transition-colors p-2 rounded-md' onClick={() => {
player.setProgress(line.time / 1000);
}}>
<span className={`color-white transition-all font-size-xl lg:font-size-2xl font-bold ${highlight ? 'opacity-90 font-size-2xl lg:font-size-3xl' : 'blur-1 opacity-20'}`}>
{line.content}
</span>
{line.translation && (
<span className={`color-white transition-all font-size-sm font-600 ${highlight ? 'opacity-90 lg:font-size-lg' : 'blur-1 opacity-20'}`}>
{line.translation}
</span>
)}
</div>
);
}}
itemContent={renderItem}
/>
);
}
12 changes: 9 additions & 3 deletions src/components/now-playing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,16 @@ export default function NowPlaying () {
</div>
{(localFullscreen || isAnimating) && (
<div
className={`translate-z-0 absolute top-0 left-0 w-full h-full ms-bezier bg-cover animate-duration-300 ${localFullscreen ? 'animate-slide-in-up' : 'animate-slide-out-down'}`}
style={{ backgroundImage: `url("${song.cover}")` }}
className={`translate-z-0 absolute top-0 left-0 w-full h-full ms-bezier animate-duration-300 overflow-hidden ${localFullscreen ? 'animate-slide-in-up' : 'animate-slide-out-down'}`}
>
<div className='w-full h-full translate-z-0 backdrop-filter backdrop-blur-256 bg-black bg-op-40 flex flex-col items-center justify-center'>
<div
className='absolute inset-[-10px] bg-cover blur-96 scale-180'
style={{ backgroundImage: `url("${song.cover}")` }}
/>
<div
className='absolute inset-0 bg-black bg-op-40'
/>
<div className='relative w-full h-full flex flex-col items-center justify-center'>
<div className='flex gap-12'>
<img draggable={false} src={song.cover} className='shadow-md border-outline-pri rounded-md w-30vw lg:w-80 object-cover aspect-square' />
{song.lyrics && <Lyrics lyrics={song.lyrics} className='h-60 w-50vw max-w-50vw lg:w-120 lg:max-w-120 overflow-x-hidden' />}
Expand Down

0 comments on commit 8f2f2b4

Please sign in to comment.