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.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.lerp import androidx.compose.ui.graphics.lerp
import kotlin.math.roundToInt import kotlin.math.roundToInt
@@ -40,12 +41,14 @@ fun rememberAnimatedArtworkColors(
): ArtworkColors { ): ArtworkColors {
var from by remember { mutableStateOf(target ?: base) } var from by remember { mutableStateOf(target ?: base) }
var to 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 progress = remember { Animatable(1f) }
val currentBase by rememberUpdatedState(base)
LaunchedEffect(target, base, durationMillis) { LaunchedEffect(target, durationMillis) {
val next = target ?: base val next = target ?: currentBase
if (next !== to) { if (next === to) return@LaunchedEffect
if (to === base) { if (onBase) {
from = next from = next
to = next to = next
} else { } else {
@@ -54,7 +57,7 @@ fun rememberAnimatedArtworkColors(
progress.snapTo(0f) progress.snapTo(0f)
progress.animateTo(1f, tween(durationMillis = durationMillis)) progress.animateTo(1f, tween(durationMillis = durationMillis))
} }
} onBase = target == null
} }
if (from === to) return to if (from === to) return to
@@ -7,8 +7,10 @@ import androidx.activity.ComponentActivity
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.SideEffect import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.luminance import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
@@ -89,6 +91,47 @@ class ArtworkExtractionTest {
assertEquals(artworkColorsFromSeed(placeholderSeed("Kid A")).scheme.primary, colors?.scheme?.primary) 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 @Test
fun keepsContentStateWhenArtworkArrives() { fun keepsContentStateWhenArtworkArrives() {
val artwork = mutableStateOf<ArtworkColors?>(null) val artwork = mutableStateOf<ArtworkColors?>(null)