feat: add fade-in and shorten restart pause for ticker

Text now fades in over 300ms when it reappears instead of popping in.
Restart pause shortened from 1.5s to 0.5s. Initial 1.5s delay kept.
Switched from infiniteRepeatable to Animatable + LaunchedEffect loop
for independent control over initial vs subsequent cycles.

Made-with: Cursor
This commit is contained in:
cottongin
2026-03-18 04:10:23 -04:00
parent 9bd38224b1
commit 600687ab56

View File

@@ -5,13 +5,10 @@ import android.content.res.Configuration
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.keyframes
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import kotlinx.coroutines.delay
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith import androidx.compose.animation.togetherWith
@@ -478,30 +475,36 @@ private fun TickerText(
val velocityPxPerMs = with(density) { velocityDpPerSecond.dp.toPx() } / 1000f val velocityPxPerMs = with(density) { velocityDpPerSecond.dp.toPx() } / 1000f
val totalScrollPx = textWidthPx + containerWidthPx val totalScrollPx = textWidthPx + containerWidthPx
val scrollMs = (totalScrollPx / velocityPxPerMs).toInt() val scrollMs = (totalScrollPx / velocityPxPerMs).toInt()
val initialDelayMs = 1500
val durationMs = initialDelayMs + scrollMs
val transition = rememberInfiniteTransition(label = "ticker") val offset = remember { Animatable(0f) }
val offset by transition.animateFloat( val alpha = remember { Animatable(0f) }
initialValue = 0f,
targetValue = 0f, LaunchedEffect(textWidthPx, containerWidthPx) {
animationSpec = infiniteRepeatable( offset.snapTo(0f)
animation = keyframes { alpha.snapTo(0f)
durationMillis = durationMs alpha.animateTo(1f, tween(300))
0f at 0 using LinearEasing delay(1500L)
0f at initialDelayMs using LinearEasing
-totalScrollPx at durationMs using LinearEasing while (true) {
}, offset.animateTo(
repeatMode = RepeatMode.Restart -totalScrollPx,
), tween(scrollMs, easing = LinearEasing)
label = "ticker" )
) offset.snapTo(0f)
alpha.snapTo(0f)
alpha.animateTo(1f, tween(300))
delay(500L)
}
}
Box( Box(
modifier = Modifier modifier = Modifier
.wrapContentWidth(align = Alignment.Start, unbounded = true) .wrapContentWidth(align = Alignment.Start, unbounded = true)
.align(Alignment.CenterStart) .align(Alignment.CenterStart)
.graphicsLayer { translationX = offset } .graphicsLayer {
translationX = offset.value
this.alpha = alpha.value
}
) { ) {
if (strokeColor != null) { if (strokeColor != null) {
Text( Text(