mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
reduce theme-transition and album scroll recomposition
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user