From eacce487df903afd83d653ad001c48b092349c9c Mon Sep 17 00:00:00 2001 From: Honey Date: Wed, 17 Jun 2026 01:39:51 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20lyrics=20scroll=20=E2=80=94=20center=20a?= =?UTF-8?q?ctive=20line=20only,=20remove=20drift=20phase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'drift to next line' animation moved the text while the user was still reading/singing the current line, making it feel out of sync. Now matches the web behavior: smooth-scroll to center the active line when it changes, nothing more. Co-Authored-By: Claude Sonnet 4.6 --- .../novasonic/app/ui/screen/NowPlayingScreen.kt | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/android/app/src/main/kotlin/com/novasonic/app/ui/screen/NowPlayingScreen.kt b/android/app/src/main/kotlin/com/novasonic/app/ui/screen/NowPlayingScreen.kt index 09c621c..9b3a24c 100644 --- a/android/app/src/main/kotlin/com/novasonic/app/ui/screen/NowPlayingScreen.kt +++ b/android/app/src/main/kotlin/com/novasonic/app/ui/screen/NowPlayingScreen.kt @@ -411,21 +411,12 @@ fun NowPlayingScreen( lyricsState.lines.indexOfLast { it.timeMs <= state.positionMs + 200L }.coerceAtLeast(0) else -1 - // Smooth continuous scroll: snap to active line, then drift toward the next + // Center the active line when it changes — matches web behavior (no drift) LaunchedEffect(activeIdx) { if (activeIdx < 0 || lyricsState.lines.isEmpty() || lyricsViewH == 0) return@LaunchedEffect - - fun targetFor(idx: Int) = (lyricsPaddingPx + idx * lineHeightPx + lineHeightPx / 2f - - lyricsViewH * 0.30f).coerceAtLeast(0f).toInt() - - // Phase 1: snap to the active line - lyricsScrollState.animateScrollTo(targetFor(activeIdx), tween(380, easing = FastOutSlowInEasing)) - - // Phase 2: drift to the next line's position over this line's remaining duration - val nextLine = lyricsState.lines.getOrNull(activeIdx + 1) ?: return@LaunchedEffect - val thisLine = lyricsState.lines[activeIdx] - val driftMs = (nextLine.timeMs - thisLine.timeMs - 380L).coerceIn(50L, 12_000L) - lyricsScrollState.animateScrollTo(targetFor(activeIdx + 1), tween(driftMs.toInt(), easing = LinearEasing)) + val target = (lyricsPaddingPx + activeIdx * lineHeightPx + lineHeightPx / 2f + - lyricsViewH * 0.5f).coerceAtLeast(0f).toInt() + lyricsScrollState.animateScrollTo(target, tween(350, easing = FastOutSlowInEasing)) } when {