reduce theme-transition and album scroll recomposition

This commit is contained in:
austinried
2026-10-04 00:29:53 +00:00
parent eb6383115e
commit f32135d8d2
3 changed files with 64 additions and 17 deletions
@@ -48,11 +48,12 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.onSizeChanged
@@ -122,14 +123,15 @@ fun AlbumDetailScreen(
playingSongId: String? = null,
modifier: Modifier = Modifier,
) {
ArtworkTheme(artwork) {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) {
ArtworkTheme(artwork) {
BoxWithConstraints(modifier.fillMaxSize().background(Color.Black)) {
val listState = rememberLazyListState()
val fill = rememberViewportFill(listState)
val density = LocalDensity.current
val gradientHeight = maxHeight * GRADIENT_SCREENS
val gradientHeightPx = with(density) { gradientHeight.toPx() }
val screenHeightPx = with(density) { maxHeight.toPx() }
val statusBarTop = WindowInsets.statusBars.getTop(density)
val navBarBottom = WindowInsets.navigationBars.getBottom(density)
val statusBarDp = with(density) { statusBarTop.toDp() }
@@ -155,16 +157,20 @@ fun AlbumDetailScreen(
}
}
}
val barColor =
if (artwork == null || gradientHeightPx <= 0f) {
Color.Black
} else {
val mid = scrollPx + barHeightPx / 2f
lerp(
artwork.gradientColorAt(mid / gradientHeightPx),
artwork.darkPrimary,
heroDarkenAt(mid, with(density) { maxHeight.toPx() }),
)
val barColor by
remember(artwork, barHeightPx, gradientHeightPx, screenHeightPx) {
derivedStateOf {
if (artwork == null || gradientHeightPx <= 0f) {
Color.Black
} else {
val mid = scrollPx + barHeightPx / 2f
lerp(
artwork.gradientColorAt(mid / gradientHeightPx),
artwork.darkPrimary,
heroDarkenAt(mid, screenHeightPx),
)
}
}
}
HeroGradient(
@@ -220,7 +226,7 @@ fun AlbumDetailScreen(
Brush.verticalGradient(
listOf(Color.Black.copy(alpha = 0.55f), Color.Transparent),
),
).alpha(1f - barFraction),
).graphicsLayer { alpha = 1f - barFraction },
)
TopAppBar(
@@ -230,7 +236,7 @@ fun AlbumDetailScreen(
style = MaterialTheme.typography.headlineMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.alpha(barFraction),
modifier = Modifier.graphicsLayer { alpha = barFraction },
)
},
navigationIcon = {
@@ -242,7 +248,7 @@ fun AlbumDetailScreen(
modifier =
Modifier
.align(Alignment.TopStart)
.background(barColor.copy(alpha = barFraction))
.drawBehind { drawRect(barColor.copy(alpha = barFraction)) }
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
@@ -10,9 +10,12 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.lerp
import kotlin.math.roundToInt
const val ARTWORK_THEME_TRANSITION_MS = 600
private const val TRANSITION_STEPS = 24
private val baseArtworkColors =
ArtworkColors(
scheme = SubtracksColorScheme,
@@ -42,7 +45,8 @@ fun rememberAnimatedArtworkColors(
}
}
val fraction = progress.value
if (from === to) return to
val fraction = (progress.value * TRANSITION_STEPS).roundToInt() / TRANSITION_STEPS.toFloat()
return remember(from, to, fraction) { lerpArtworkColors(from, to, fraction) }
}
@@ -79,4 +83,39 @@ private fun lerpScheme(
onTertiary = lerp(a.onTertiary, b.onTertiary, t),
tertiaryContainer = lerp(a.tertiaryContainer, b.tertiaryContainer, t),
onTertiaryContainer = lerp(a.onTertiaryContainer, b.onTertiaryContainer, t),
background = lerp(a.background, b.background, t),
onBackground = lerp(a.onBackground, b.onBackground, t),
surface = lerp(a.surface, b.surface, t),
onSurface = lerp(a.onSurface, b.onSurface, t),
surfaceVariant = lerp(a.surfaceVariant, b.surfaceVariant, t),
onSurfaceVariant = lerp(a.onSurfaceVariant, b.onSurfaceVariant, t),
surfaceTint = lerp(a.surfaceTint, b.surfaceTint, t),
inverseSurface = lerp(a.inverseSurface, b.inverseSurface, t),
inverseOnSurface = lerp(a.inverseOnSurface, b.inverseOnSurface, t),
error = lerp(a.error, b.error, t),
onError = lerp(a.onError, b.onError, t),
errorContainer = lerp(a.errorContainer, b.errorContainer, t),
onErrorContainer = lerp(a.onErrorContainer, b.onErrorContainer, t),
outline = lerp(a.outline, b.outline, t),
outlineVariant = lerp(a.outlineVariant, b.outlineVariant, t),
scrim = lerp(a.scrim, b.scrim, t),
surfaceBright = lerp(a.surfaceBright, b.surfaceBright, t),
surfaceDim = lerp(a.surfaceDim, b.surfaceDim, t),
surfaceContainer = lerp(a.surfaceContainer, b.surfaceContainer, t),
surfaceContainerHigh = lerp(a.surfaceContainerHigh, b.surfaceContainerHigh, t),
surfaceContainerHighest = lerp(a.surfaceContainerHighest, b.surfaceContainerHighest, t),
surfaceContainerLow = lerp(a.surfaceContainerLow, b.surfaceContainerLow, t),
surfaceContainerLowest = lerp(a.surfaceContainerLowest, b.surfaceContainerLowest, t),
primaryFixed = lerp(a.primaryFixed, b.primaryFixed, t),
primaryFixedDim = lerp(a.primaryFixedDim, b.primaryFixedDim, t),
onPrimaryFixed = lerp(a.onPrimaryFixed, b.onPrimaryFixed, t),
onPrimaryFixedVariant = lerp(a.onPrimaryFixedVariant, b.onPrimaryFixedVariant, t),
secondaryFixed = lerp(a.secondaryFixed, b.secondaryFixed, t),
secondaryFixedDim = lerp(a.secondaryFixedDim, b.secondaryFixedDim, t),
onSecondaryFixed = lerp(a.onSecondaryFixed, b.onSecondaryFixed, t),
onSecondaryFixedVariant = lerp(a.onSecondaryFixedVariant, b.onSecondaryFixedVariant, t),
tertiaryFixed = lerp(a.tertiaryFixed, b.tertiaryFixed, t),
tertiaryFixedDim = lerp(a.tertiaryFixedDim, b.tertiaryFixedDim, t),
onTertiaryFixed = lerp(a.onTertiaryFixed, b.onTertiaryFixed, t),
onTertiaryFixedVariant = lerp(a.onTertiaryFixedVariant, b.onTertiaryFixedVariant, t),
)
@@ -5,6 +5,7 @@ import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
@@ -31,6 +32,7 @@ private const val DARKEN_START_SCREENS = 0.5f
private const val DARKEN_END_SCREENS = 1.5f
private const val DARKEN_KNEE = 0.2f
@Immutable
data class ArtworkColors(
val scheme: ColorScheme,
val gradientHigh: Color,