keep the artwork transition animating when the base scheme changes

This commit is contained in:
austinried
2026-10-04 00:30:05 +00:00
parent f33266113b
commit 430d2cf104
2 changed files with 58 additions and 12 deletions
@@ -9,6 +9,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.lerp
import kotlin.math.roundToInt
@@ -40,21 +41,23 @@ fun rememberAnimatedArtworkColors(
): ArtworkColors {
var from by remember { mutableStateOf(target ?: base) }
var to by remember { mutableStateOf(target ?: base) }
var onBase by remember { mutableStateOf(target == null) }
val progress = remember { Animatable(1f) }
val currentBase by rememberUpdatedState(base)
LaunchedEffect(target, base, durationMillis) {
val next = target ?: base
if (next !== to) {
if (to === base) {
from = next
to = next
} else {
from = lerpArtworkColors(from, to, progress.value)
to = next
progress.snapTo(0f)
progress.animateTo(1f, tween(durationMillis = durationMillis))
}
LaunchedEffect(target, durationMillis) {
val next = target ?: currentBase
if (next === to) return@LaunchedEffect
if (onBase) {
from = next
to = next
} else {
from = lerpArtworkColors(from, to, progress.value)
to = next
progress.snapTo(0f)
progress.animateTo(1f, tween(durationMillis = durationMillis))
}
onBase = target == null
}
if (from === to) return to
@@ -7,8 +7,10 @@ import androidx.activity.ComponentActivity
import androidx.compose.material3.Text
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule
import androidx.test.core.app.ApplicationProvider
@@ -89,6 +91,47 @@ class ArtworkExtractionTest {
assertEquals(artworkColorsFromSeed(placeholderSeed("Kid A")).scheme.primary, colors?.scheme?.primary)
}
@Test
fun fadesFromThePlaceholderToTheArtworkSeed() {
val placeholder = artworkColorsFromSeed(placeholderSeed("Kid A"))
val art = artworkColorsFromSeed(0xFF3A7BD5.toInt())
val artwork = mutableStateOf(placeholder)
val seen = mutableListOf<androidx.compose.ui.graphics.Color>()
composeRule.setContent {
SubtracksTheme {
val colors = rememberAnimatedArtworkColors(artwork.value, baseArtworkColors())
SideEffect { seen += colors.scheme.background }
}
}
composeRule.waitForIdle()
composeRule.runOnIdle { artwork.value = art }
composeRule.waitForIdle()
assertEquals(art.scheme.background, seen.last())
}
@Test
fun aChangingBaseSchemeDoesNotStrandTheArtworkTransition() {
val placeholder = artworkColorsFromSeed(placeholderSeed("Kid A"))
val art = artworkColorsFromSeed(0xFF3A7BD5.toInt())
val artwork = mutableStateOf(placeholder)
var baseTick by mutableStateOf(0)
val seen = mutableListOf<androidx.compose.ui.graphics.Color>()
composeRule.setContent {
SubtracksTheme {
val base = remember(baseTick) { artworkColorsFromSeed(baseTick) }
val colors = rememberAnimatedArtworkColors(artwork.value, base)
SideEffect {
seen += colors.scheme.background
if (artwork.value !== placeholder && baseTick < 20) baseTick++
}
}
}
composeRule.waitForIdle()
composeRule.runOnIdle { artwork.value = art }
composeRule.waitForIdle()
assertEquals(art.scheme.background, seen.last())
}
@Test
fun keepsContentStateWhenArtworkArrives() {
val artwork = mutableStateOf<ArtworkColors?>(null)