mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 23:21:59 +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.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.draw.drawBehind
|
||||||
import androidx.compose.ui.draw.shadow
|
import androidx.compose.ui.draw.shadow
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.graphics.lerp
|
import androidx.compose.ui.graphics.lerp
|
||||||
import androidx.compose.ui.layout.onGloballyPositioned
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.layout.onSizeChanged
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
@@ -122,14 +123,15 @@ fun AlbumDetailScreen(
|
|||||||
playingSongId: String? = null,
|
playingSongId: String? = null,
|
||||||
modifier: Modifier = Modifier,
|
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)) {
|
BoxWithConstraints(modifier.fillMaxSize().background(Color.Black)) {
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
val fill = rememberViewportFill(listState)
|
val fill = rememberViewportFill(listState)
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
val gradientHeight = maxHeight * GRADIENT_SCREENS
|
val gradientHeight = maxHeight * GRADIENT_SCREENS
|
||||||
val gradientHeightPx = with(density) { gradientHeight.toPx() }
|
val gradientHeightPx = with(density) { gradientHeight.toPx() }
|
||||||
|
val screenHeightPx = with(density) { maxHeight.toPx() }
|
||||||
val statusBarTop = WindowInsets.statusBars.getTop(density)
|
val statusBarTop = WindowInsets.statusBars.getTop(density)
|
||||||
val navBarBottom = WindowInsets.navigationBars.getBottom(density)
|
val navBarBottom = WindowInsets.navigationBars.getBottom(density)
|
||||||
val statusBarDp = with(density) { statusBarTop.toDp() }
|
val statusBarDp = with(density) { statusBarTop.toDp() }
|
||||||
@@ -155,16 +157,20 @@ fun AlbumDetailScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val barColor =
|
val barColor by
|
||||||
if (artwork == null || gradientHeightPx <= 0f) {
|
remember(artwork, barHeightPx, gradientHeightPx, screenHeightPx) {
|
||||||
Color.Black
|
derivedStateOf {
|
||||||
} else {
|
if (artwork == null || gradientHeightPx <= 0f) {
|
||||||
val mid = scrollPx + barHeightPx / 2f
|
Color.Black
|
||||||
lerp(
|
} else {
|
||||||
artwork.gradientColorAt(mid / gradientHeightPx),
|
val mid = scrollPx + barHeightPx / 2f
|
||||||
artwork.darkPrimary,
|
lerp(
|
||||||
heroDarkenAt(mid, with(density) { maxHeight.toPx() }),
|
artwork.gradientColorAt(mid / gradientHeightPx),
|
||||||
)
|
artwork.darkPrimary,
|
||||||
|
heroDarkenAt(mid, screenHeightPx),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HeroGradient(
|
HeroGradient(
|
||||||
@@ -220,7 +226,7 @@ fun AlbumDetailScreen(
|
|||||||
Brush.verticalGradient(
|
Brush.verticalGradient(
|
||||||
listOf(Color.Black.copy(alpha = 0.55f), Color.Transparent),
|
listOf(Color.Black.copy(alpha = 0.55f), Color.Transparent),
|
||||||
),
|
),
|
||||||
).alpha(1f - barFraction),
|
).graphicsLayer { alpha = 1f - barFraction },
|
||||||
)
|
)
|
||||||
|
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
@@ -230,7 +236,7 @@ fun AlbumDetailScreen(
|
|||||||
style = MaterialTheme.typography.headlineMedium,
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier = Modifier.alpha(barFraction),
|
modifier = Modifier.graphicsLayer { alpha = barFraction },
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
@@ -242,7 +248,7 @@ fun AlbumDetailScreen(
|
|||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.align(Alignment.TopStart)
|
.align(Alignment.TopStart)
|
||||||
.background(barColor.copy(alpha = barFraction))
|
.drawBehind { drawRect(barColor.copy(alpha = barFraction)) }
|
||||||
.clickable(
|
.clickable(
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
indication = null,
|
indication = null,
|
||||||
|
|||||||
@@ -10,9 +10,12 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
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
|
||||||
|
|
||||||
const val ARTWORK_THEME_TRANSITION_MS = 600
|
const val ARTWORK_THEME_TRANSITION_MS = 600
|
||||||
|
|
||||||
|
private const val TRANSITION_STEPS = 24
|
||||||
|
|
||||||
private val baseArtworkColors =
|
private val baseArtworkColors =
|
||||||
ArtworkColors(
|
ArtworkColors(
|
||||||
scheme = SubtracksColorScheme,
|
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) }
|
return remember(from, to, fraction) { lerpArtworkColors(from, to, fraction) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,4 +83,39 @@ private fun lerpScheme(
|
|||||||
onTertiary = lerp(a.onTertiary, b.onTertiary, t),
|
onTertiary = lerp(a.onTertiary, b.onTertiary, t),
|
||||||
tertiaryContainer = lerp(a.tertiaryContainer, b.tertiaryContainer, t),
|
tertiaryContainer = lerp(a.tertiaryContainer, b.tertiaryContainer, t),
|
||||||
onTertiaryContainer = lerp(a.onTertiaryContainer, b.onTertiaryContainer, 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.MaterialTheme
|
||||||
import androidx.compose.material3.darkColorScheme
|
import androidx.compose.material3.darkColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.produceState
|
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_END_SCREENS = 1.5f
|
||||||
private const val DARKEN_KNEE = 0.2f
|
private const val DARKEN_KNEE = 0.2f
|
||||||
|
|
||||||
|
@Immutable
|
||||||
data class ArtworkColors(
|
data class ArtworkColors(
|
||||||
val scheme: ColorScheme,
|
val scheme: ColorScheme,
|
||||||
val gradientHigh: Color,
|
val gradientHigh: Color,
|
||||||
|
|||||||
Reference in New Issue
Block a user