diff --git a/app/src/main/kotlin/com/subtracks/ui/library/AlbumDetailScreen.kt b/app/src/main/kotlin/com/subtracks/ui/library/AlbumDetailScreen.kt index 4b4d19a5..58da61cf 100644 --- a/app/src/main/kotlin/com/subtracks/ui/library/AlbumDetailScreen.kt +++ b/app/src/main/kotlin/com/subtracks/ui/library/AlbumDetailScreen.kt @@ -80,6 +80,7 @@ import com.subtracks.ui.theme.ArtworkColors import com.subtracks.ui.theme.ArtworkTheme import com.subtracks.ui.theme.HeroGradient import com.subtracks.ui.theme.gradientColorAt +import com.subtracks.ui.theme.heroDarkenAt import com.subtracks.ui.theme.rememberArtworkColors import org.koin.compose.koinInject import org.koin.compose.viewmodel.koinViewModel @@ -166,7 +167,14 @@ fun AlbumDetailScreen( label = "gradientAlpha", ) val barColor = - artwork?.gradientColorAt((scrollPx + barHeightPx / 2f) / gradientHeightPx) ?: Color.Black + artwork?.let { + val mid = scrollPx + barHeightPx / 2f + lerp( + it.gradientColorAt(mid / gradientHeightPx), + Color.Black, + heroDarkenAt(mid, with(density) { maxHeight.toPx() }), + ) + } ?: Color.Black HeroGradient( colors = artwork, diff --git a/app/src/main/kotlin/com/subtracks/ui/theme/ArtworkTheme.kt b/app/src/main/kotlin/com/subtracks/ui/theme/ArtworkTheme.kt index 6643840f..003f26c3 100644 --- a/app/src/main/kotlin/com/subtracks/ui/theme/ArtworkTheme.kt +++ b/app/src/main/kotlin/com/subtracks/ui/theme/ArtworkTheme.kt @@ -36,10 +36,10 @@ private const val PERIOD_SCREENS = 2f private const val BLOB_ZONE = 0.62f private const val BLOB_COUNT = 4 private const val ACCENT_MAX_LUMINANCE = 0.34f -private const val HERO_DARKEN_MAX = 0.85f -private const val HERO_DARKEN_SCREENS = 1.5f -private const val BASE_BOTTOM_DARKEN = 0.6f -private const val POSITION_FADE_START = 0.55f +private const val HERO_DARKEN_MAX = 0.8f +private const val DARKEN_START_SCREENS = 0.5f +private const val DARKEN_END_SCREENS = 1.5f +private const val DARKEN_KNEE = 0.2f data class ArtworkColors( val scheme: ColorScheme, @@ -284,22 +284,39 @@ fun HeroGradient( } } } - val progress = (scroll / (size.height * HERO_DARKEN_SCREENS)).coerceIn(0f, 1f) - val bottomAlpha = BASE_BOTTOM_DARKEN + (HERO_DARKEN_MAX - BASE_BOTTOM_DARKEN) * progress + val darkenStart = size.height * DARKEN_START_SCREENS - scroll + val darkenEnd = size.height * DARKEN_END_SCREENS - scroll drawRect( brush = Brush.verticalGradient( colorStops = arrayOf( 0f to Color.Transparent, - POSITION_FADE_START to Color.Transparent, - 1f to Color.Black.copy(alpha = bottomAlpha), + DARKEN_KNEE to Color.Black.copy(alpha = HERO_DARKEN_MAX * 0.5f), + 1f to Color.Black.copy(alpha = HERO_DARKEN_MAX), ), + startY = darkenStart, + endY = darkenEnd, + tileMode = TileMode.Clamp, ), ) } } +fun heroDarkenAt( + contentY: Float, + screenHeightPx: Float, +): Float { + val start = screenHeightPx * DARKEN_START_SCREENS + val end = screenHeightPx * DARKEN_END_SCREENS + val fraction = ((contentY - start) / (end - start)).coerceIn(0f, 1f) + return if (fraction <= DARKEN_KNEE) { + HERO_DARKEN_MAX * 0.5f * (fraction / DARKEN_KNEE) + } else { + HERO_DARKEN_MAX * 0.5f + HERO_DARKEN_MAX * 0.5f * ((fraction - DARKEN_KNEE) / (1f - DARKEN_KNEE)) + } +} + private fun Color.toHsl(): Triple { val max = maxOf(red, green, blue) val min = minOf(red, green, blue)