feat: replace bounce marquee with constant-speed ticker

Replaces BounceMarqueeText with TickerText that scrolls at a fixed
33 dp/s regardless of text length. Text scrolls left off-screen with
a container-width gap before looping. Initial 1.5s delay lets the
user read the beginning.

Made-with: Cursor
This commit is contained in:
cottongin
2026-03-12 05:49:28 -04:00
parent 5d551c9380
commit 9bd38224b1

View File

@@ -5,7 +5,6 @@ import android.content.res.Configuration
import android.graphics.BitmapFactory
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
@@ -447,12 +446,13 @@ private fun ArtworkImage(
}
@Composable
private fun BounceMarqueeText(
private fun TickerText(
text: String,
style: TextStyle,
color: Color,
strokeColor: Color? = null,
strokeWidth: Float = 2f,
velocityDpPerSecond: Float = 33f,
modifier: Modifier = Modifier
) {
val density = LocalDensity.current
@@ -475,25 +475,26 @@ private fun BounceMarqueeText(
if (overflowPx > 0f) {
key(text) {
val scrollMs = ((overflowPx / density.density) * 18f).toInt().coerceIn(2000, 8000)
val pauseMs = 1500
val totalMs = pauseMs + scrollMs + pauseMs + scrollMs
val velocityPxPerMs = with(density) { velocityDpPerSecond.dp.toPx() } / 1000f
val totalScrollPx = textWidthPx + containerWidthPx
val scrollMs = (totalScrollPx / velocityPxPerMs).toInt()
val initialDelayMs = 1500
val durationMs = initialDelayMs + scrollMs
val transition = rememberInfiniteTransition(label = "marquee")
val transition = rememberInfiniteTransition(label = "ticker")
val offset by transition.animateFloat(
initialValue = 0f,
targetValue = 0f,
animationSpec = infiniteRepeatable(
animation = keyframes {
durationMillis = totalMs
durationMillis = durationMs
0f at 0 using LinearEasing
0f at pauseMs using FastOutSlowInEasing
-overflowPx at (pauseMs + scrollMs) using LinearEasing
-overflowPx at (pauseMs + scrollMs + pauseMs) using FastOutSlowInEasing
0f at initialDelayMs using LinearEasing
-totalScrollPx at durationMs using LinearEasing
},
repeatMode = RepeatMode.Restart
),
label = "bounce"
label = "ticker"
)
Box(
@@ -575,7 +576,7 @@ private fun TrackInfoSection(
overflow = TextOverflow.Ellipsis
)
Spacer(modifier = Modifier.height(6.dp))
BounceMarqueeText(
TickerText(
text = trackText,
style = trackStyle,
color = trackColor,