mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-22 12:08:34 +00:00
AppScaffold Animation Performance impromements.
This commit is contained in:
committed by
jeffrey-signal
parent
443463aca8
commit
ae8b8bbe7c
@@ -25,6 +25,7 @@ import androidx.compose.runtime.snapshotFlow
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.drawWithContent
|
import androidx.compose.ui.draw.drawWithContent
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.graphics.layer.GraphicsLayer
|
import androidx.compose.ui.graphics.layer.GraphicsLayer
|
||||||
import androidx.compose.ui.graphics.layer.drawLayer
|
import androidx.compose.ui.graphics.layer.drawLayer
|
||||||
import androidx.compose.ui.graphics.rememberGraphicsLayer
|
import androidx.compose.ui.graphics.rememberGraphicsLayer
|
||||||
@@ -47,7 +48,6 @@ import org.thoughtcrime.securesms.conversation.v2.ConversationFragment
|
|||||||
import org.thoughtcrime.securesms.serialization.JsonSerializableNavType
|
import org.thoughtcrime.securesms.serialization.JsonSerializableNavType
|
||||||
import org.thoughtcrime.securesms.window.AppScaffoldAnimationDefaults
|
import org.thoughtcrime.securesms.window.AppScaffoldAnimationDefaults
|
||||||
import org.thoughtcrime.securesms.window.AppScaffoldAnimationState
|
import org.thoughtcrime.securesms.window.AppScaffoldAnimationState
|
||||||
import org.thoughtcrime.securesms.window.AppScaffoldNavigator
|
|
||||||
import org.thoughtcrime.securesms.window.WindowSizeClass
|
import org.thoughtcrime.securesms.window.WindowSizeClass
|
||||||
import kotlin.reflect.typeOf
|
import kotlin.reflect.typeOf
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
@@ -90,7 +90,11 @@ fun NavGraphBuilder.chatNavGraphBuilder(
|
|||||||
bitmap = bitmap,
|
bitmap = bitmap,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.then(fakeChatListAnimationState.toModifier())
|
.graphicsLayer {
|
||||||
|
with(fakeChatListAnimationState) {
|
||||||
|
applyChildValues()
|
||||||
|
}
|
||||||
|
}
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -100,7 +104,11 @@ fun NavGraphBuilder.chatNavGraphBuilder(
|
|||||||
fragmentState = fragmentState,
|
fragmentState = fragmentState,
|
||||||
arguments = requireNotNull(ConversationIntents.createBuilderSync(context, route.conversationArgs).build().extras) { "Handed null Conversation intent arguments." },
|
arguments = requireNotNull(ConversationIntents.createBuilderSync(context, route.conversationArgs).build().extras) { "Handed null Conversation intent arguments." },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.then(chatAnimationState.toModifier())
|
.graphicsLayer {
|
||||||
|
with(chatAnimationState) {
|
||||||
|
applyChildValues()
|
||||||
|
}
|
||||||
|
}
|
||||||
.background(MaterialTheme.colorScheme.background)
|
.background(MaterialTheme.colorScheme.background)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
) { fragment ->
|
) { fragment ->
|
||||||
@@ -129,35 +137,38 @@ fun NavGraphBuilder.chatNavGraphBuilder(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun Transition<Boolean>.fakeChatListAnimationState(): AppScaffoldAnimationState {
|
private fun Transition<Boolean>.fakeChatListAnimationState(): AppScaffoldAnimationState {
|
||||||
val alpha by animateFloat(transitionSpec = { AppScaffoldAnimationDefaults.tween() }) { if (it) 0f else 1f }
|
val alpha = animateFloat(transitionSpec = { AppScaffoldAnimationDefaults.tween() }) { if (it) 0f else 1f }
|
||||||
val offset by animateDp(transitionSpec = { AppScaffoldAnimationDefaults.tween() }) { if (it) (-48).dp else 0.dp }
|
val offset = animateDp(transitionSpec = { AppScaffoldAnimationDefaults.tween() }) { if (it) (-48).dp else 0.dp }
|
||||||
|
|
||||||
return AppScaffoldAnimationState(
|
return remember {
|
||||||
navigationState = AppScaffoldNavigator.NavigationState.ENTER,
|
AppScaffoldAnimationState(
|
||||||
offset = offset,
|
offset = offset,
|
||||||
alpha = alpha
|
alpha = alpha
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun Transition<Boolean>.chatAnimationState(hasFake: Boolean): AppScaffoldAnimationState {
|
private fun Transition<Boolean>.chatAnimationState(hasFake: Boolean): AppScaffoldAnimationState {
|
||||||
val alpha by animateFloat(transitionSpec = { AppScaffoldAnimationDefaults.tween() }) { if (it) 1f else 0f }
|
val alpha = animateFloat(transitionSpec = { AppScaffoldAnimationDefaults.tween() }) { if (it) 1f else 0f }
|
||||||
|
|
||||||
return if (!hasFake) {
|
return if (!hasFake) {
|
||||||
|
remember {
|
||||||
AppScaffoldAnimationState(
|
AppScaffoldAnimationState(
|
||||||
navigationState = AppScaffoldNavigator.NavigationState.ENTER,
|
offset = mutableStateOf(0.dp),
|
||||||
offset = 0.dp,
|
|
||||||
alpha = alpha
|
alpha = alpha
|
||||||
)
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
val offset by animateDp(transitionSpec = { AppScaffoldAnimationDefaults.tween() }) { if (it) 0.dp else 48.dp }
|
val offset = animateDp(transitionSpec = { AppScaffoldAnimationDefaults.tween() }) { if (it) 0.dp else 48.dp }
|
||||||
|
|
||||||
|
remember {
|
||||||
AppScaffoldAnimationState(
|
AppScaffoldAnimationState(
|
||||||
navigationState = AppScaffoldNavigator.NavigationState.ENTER,
|
|
||||||
offset = offset,
|
offset = offset,
|
||||||
alpha = alpha
|
alpha = alpha
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ import androidx.compose.runtime.remember
|
|||||||
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.clipToBounds
|
import androidx.compose.ui.draw.clipToBounds
|
||||||
|
import androidx.compose.ui.draw.drawWithContent
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.layout.layout
|
import androidx.compose.ui.layout.layout
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.platform.LocalInspectionMode
|
import androidx.compose.ui.platform.LocalInspectionMode
|
||||||
@@ -277,11 +279,19 @@ fun AppScaffold(
|
|||||||
exitTransition = ExitTransition.None,
|
exitTransition = ExitTransition.None,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.zIndex(0f)
|
.zIndex(0f)
|
||||||
.then(animationState.parentModifier)
|
.drawWithContent {
|
||||||
|
with(animationState) {
|
||||||
|
applyParentValues()
|
||||||
|
}
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.then(animationState.toModifier())
|
.graphicsLayer {
|
||||||
|
with(animationState) {
|
||||||
|
applyChildValues()
|
||||||
|
}
|
||||||
|
}
|
||||||
.clipToBounds()
|
.clipToBounds()
|
||||||
.layout { measurable, constraints ->
|
.layout { measurable, constraints ->
|
||||||
val width = max(minPaneWidth.roundToPx(), constraints.maxWidth)
|
val width = max(minPaneWidth.roundToPx(), constraints.maxWidth)
|
||||||
@@ -320,11 +330,19 @@ fun AppScaffold(
|
|||||||
exitTransition = ExitTransition.None,
|
exitTransition = ExitTransition.None,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.zIndex(1f)
|
.zIndex(1f)
|
||||||
.then(animationState.parentModifier)
|
.drawWithContent {
|
||||||
|
with(animationState) {
|
||||||
|
applyParentValues()
|
||||||
|
}
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.then(animationState.toModifier())
|
.graphicsLayer {
|
||||||
|
with(animationState) {
|
||||||
|
applyChildValues()
|
||||||
|
}
|
||||||
|
}
|
||||||
.clipToBounds()
|
.clipToBounds()
|
||||||
.layout { measurable, constraints ->
|
.layout { measurable, constraints ->
|
||||||
val width = max(minPaneWidth.roundToPx(), constraints.maxWidth)
|
val width = max(minPaneWidth.roundToPx(), constraints.maxWidth)
|
||||||
|
|||||||
@@ -7,17 +7,19 @@ package org.thoughtcrime.securesms.window
|
|||||||
|
|
||||||
import androidx.compose.animation.core.CubicBezierEasing
|
import androidx.compose.animation.core.CubicBezierEasing
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.layout.offset
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||||
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope
|
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.draw.scale
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.ui.graphics.Shape
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.GraphicsLayerScope
|
||||||
|
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.coerceAtMost
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,28 +32,38 @@ object AppScaffoldAnimationDefaults {
|
|||||||
fun <T> tween() = tween<T>(durationMillis = 200, easing = TweenEasing)
|
fun <T> tween() = tween<T>(durationMillis = 200, easing = TweenEasing)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces modifier that can be composed into another modifier chain.
|
|
||||||
* This object allows us to store "latest state" as we transition.
|
|
||||||
*
|
|
||||||
* @param parentModifier This modifier is applied to the [androidx.compose.material3.adaptive.layout.AnimatedPane] itself,
|
|
||||||
* allowing additional customization like overlays without
|
|
||||||
* the need for additional composables.
|
|
||||||
*/
|
|
||||||
data class AppScaffoldAnimationState(
|
data class AppScaffoldAnimationState(
|
||||||
val navigationState: AppScaffoldNavigator.NavigationState,
|
private val alpha: State<Float> = mutableStateOf(1f),
|
||||||
val alpha: Float = 1f,
|
private val scale: State<Float> = mutableStateOf(1f),
|
||||||
val scale: Float = 1f,
|
val scaleMinimum: Float = 0f,
|
||||||
val offset: Dp = 0.dp,
|
private val offset: State<Dp> = mutableStateOf(0.dp),
|
||||||
val clipShape: Shape = RoundedCornerShape(0.dp),
|
private val corners: State<Dp> = mutableStateOf(0.dp),
|
||||||
val parentModifier: Modifier = Modifier.Companion
|
val cornersMaximum: Dp = 1000.dp,
|
||||||
|
private val parentOverlayAlpha: State<Float> = mutableStateOf(0f)
|
||||||
) {
|
) {
|
||||||
fun toModifier(): Modifier {
|
|
||||||
return Modifier
|
private val unclampedScale by scale
|
||||||
.alpha(alpha)
|
private val unclampedCorners by corners
|
||||||
.scale(scale)
|
|
||||||
.offset(offset)
|
val contentAlpha by alpha
|
||||||
.clip(clipShape)
|
val contentScale by derivedStateOf { unclampedScale.coerceAtLeast(scaleMinimum) }
|
||||||
|
val contentOffset by offset
|
||||||
|
val contentCorners by derivedStateOf { unclampedCorners.coerceAtMost(cornersMaximum) }
|
||||||
|
|
||||||
|
fun ContentDrawScope.applyParentValues() {
|
||||||
|
drawContent()
|
||||||
|
|
||||||
|
drawRect(Color(0f, 0f, 0f, parentOverlayAlpha.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun GraphicsLayerScope.applyChildValues() {
|
||||||
|
this.alpha = contentAlpha
|
||||||
|
this.scaleX = contentScale
|
||||||
|
this.scaleY = contentScale
|
||||||
|
this.translationX = contentOffset.toPx()
|
||||||
|
this.translationY = 0f
|
||||||
|
this.clip = true
|
||||||
|
this.shape = RoundedCornerShape(contentCorners)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,13 +79,12 @@ class AppScaffoldAnimationStateFactory(
|
|||||||
val Default = AppScaffoldAnimationStateFactory()
|
val Default = AppScaffoldAnimationStateFactory()
|
||||||
|
|
||||||
private val EMPTY_STATE = AppScaffoldAnimationState(
|
private val EMPTY_STATE = AppScaffoldAnimationState(
|
||||||
navigationState = AppScaffoldNavigator.NavigationState.ENTER,
|
alpha = mutableStateOf(1f)
|
||||||
alpha = 1f
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var latestListSeekState: AppScaffoldAnimationState = AppScaffoldAnimationState(AppScaffoldNavigator.NavigationState.SEEK)
|
private var latestListSeekState: AppScaffoldAnimationState = EMPTY_STATE
|
||||||
private var latestDetailSeekState: AppScaffoldAnimationState = AppScaffoldAnimationState(AppScaffoldNavigator.NavigationState.SEEK)
|
private var latestDetailSeekState: AppScaffoldAnimationState = EMPTY_STATE
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ThreePaneScaffoldPaneScope.getListAnimationState(navigationState: AppScaffoldNavigator.NavigationState): AppScaffoldAnimationState {
|
fun ThreePaneScaffoldPaneScope.getListAnimationState(navigationState: AppScaffoldNavigator.NavigationState): AppScaffoldAnimationState {
|
||||||
@@ -87,6 +98,7 @@ class AppScaffoldAnimationStateFactory(
|
|||||||
AppScaffoldNavigator.NavigationState.SEEK -> defaultListSeekAnimationState().also {
|
AppScaffoldNavigator.NavigationState.SEEK -> defaultListSeekAnimationState().also {
|
||||||
latestListSeekState = it
|
latestListSeekState = it
|
||||||
}
|
}
|
||||||
|
|
||||||
AppScaffoldNavigator.NavigationState.RELEASE -> defaultListReleaseAnimationState(latestListSeekState)
|
AppScaffoldNavigator.NavigationState.RELEASE -> defaultListReleaseAnimationState(latestListSeekState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,6 +115,7 @@ class AppScaffoldAnimationStateFactory(
|
|||||||
AppScaffoldNavigator.NavigationState.SEEK -> defaultDetailSeekAnimationState().also {
|
AppScaffoldNavigator.NavigationState.SEEK -> defaultDetailSeekAnimationState().also {
|
||||||
latestDetailSeekState = it
|
latestDetailSeekState = it
|
||||||
}
|
}
|
||||||
|
|
||||||
AppScaffoldNavigator.NavigationState.RELEASE -> defaultDetailReleaseAnimationState(latestDetailSeekState)
|
AppScaffoldNavigator.NavigationState.RELEASE -> defaultDetailReleaseAnimationState(latestDetailSeekState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,18 +11,15 @@ import androidx.compose.animation.core.Transition
|
|||||||
import androidx.compose.animation.core.animateDp
|
import androidx.compose.animation.core.animateDp
|
||||||
import androidx.compose.animation.core.animateFloat
|
import androidx.compose.animation.core.animateFloat
|
||||||
import androidx.compose.animation.core.spring
|
import androidx.compose.animation.core.spring
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||||
import androidx.compose.material3.adaptive.layout.PaneAdaptedValue
|
import androidx.compose.material3.adaptive.layout.PaneAdaptedValue
|
||||||
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope
|
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.ui.draw.drawWithContent
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.coerceAtMost
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
private const val SEEK_DAMPING_RATIO = Spring.DampingRatioNoBouncy
|
private const val SEEK_DAMPING_RATIO = Spring.DampingRatioNoBouncy
|
||||||
@@ -79,7 +76,7 @@ fun ThreePaneScaffoldPaneScope.animateFloat(
|
|||||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ThreePaneScaffoldPaneScope.defaultListInitAnimationState(): AppScaffoldAnimationState {
|
fun ThreePaneScaffoldPaneScope.defaultListInitAnimationState(): AppScaffoldAnimationState {
|
||||||
val offset by animateDp(
|
val offset = animateDp(
|
||||||
targetWhenHiding = {
|
targetWhenHiding = {
|
||||||
-AppScaffoldAnimationDefaults.InitAnimationOffset
|
-AppScaffoldAnimationDefaults.InitAnimationOffset
|
||||||
},
|
},
|
||||||
@@ -88,21 +85,22 @@ fun ThreePaneScaffoldPaneScope.defaultListInitAnimationState(): AppScaffoldAnima
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
val alpha by animateFloat {
|
val alpha = animateFloat {
|
||||||
1f
|
1f
|
||||||
}
|
}
|
||||||
|
|
||||||
return AppScaffoldAnimationState(
|
return remember {
|
||||||
AppScaffoldNavigator.NavigationState.ENTER,
|
AppScaffoldAnimationState(
|
||||||
alpha = alpha,
|
alpha = alpha,
|
||||||
offset = offset
|
offset = offset
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ThreePaneScaffoldPaneScope.defaultListSeekAnimationState(): AppScaffoldAnimationState {
|
fun ThreePaneScaffoldPaneScope.defaultListSeekAnimationState(): AppScaffoldAnimationState {
|
||||||
val scale by animateFloat(
|
val scale = animateFloat(
|
||||||
transitionSpec = {
|
transitionSpec = {
|
||||||
appScaffoldSeekSpring()
|
appScaffoldSeekSpring()
|
||||||
},
|
},
|
||||||
@@ -110,7 +108,7 @@ fun ThreePaneScaffoldPaneScope.defaultListSeekAnimationState(): AppScaffoldAnima
|
|||||||
targetWhenHiding = { 1f }
|
targetWhenHiding = { 1f }
|
||||||
)
|
)
|
||||||
|
|
||||||
val offset by animateDp(
|
val offset = animateDp(
|
||||||
transitionSpec = {
|
transitionSpec = {
|
||||||
appScaffoldSeekSpring()
|
appScaffoldSeekSpring()
|
||||||
},
|
},
|
||||||
@@ -118,52 +116,51 @@ fun ThreePaneScaffoldPaneScope.defaultListSeekAnimationState(): AppScaffoldAnima
|
|||||||
targetWhenShowing = { 0.dp }
|
targetWhenShowing = { 0.dp }
|
||||||
)
|
)
|
||||||
|
|
||||||
return AppScaffoldAnimationState(
|
return remember {
|
||||||
navigationState = AppScaffoldNavigator.NavigationState.SEEK,
|
AppScaffoldAnimationState(
|
||||||
offset = offset,
|
offset = offset,
|
||||||
scale = scale.coerceAtLeast(0.9f),
|
scale = scale,
|
||||||
parentModifier = Modifier.drawWithContent {
|
scaleMinimum = 0.9f,
|
||||||
drawContent()
|
parentOverlayAlpha = mutableStateOf(0.2f)
|
||||||
|
|
||||||
drawRect(Color(0f, 0f, 0f, 0.2f))
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ThreePaneScaffoldPaneScope.defaultListReleaseAnimationState(from: AppScaffoldAnimationState): AppScaffoldAnimationState {
|
fun ThreePaneScaffoldPaneScope.defaultListReleaseAnimationState(from: AppScaffoldAnimationState): AppScaffoldAnimationState {
|
||||||
val scale by animateFloat(
|
val initialScale = remember { from.contentScale }
|
||||||
targetWhenHiding = { from.scale },
|
val initialOffset = remember { from.contentOffset }
|
||||||
|
|
||||||
|
val scale = animateFloat(
|
||||||
|
targetWhenHiding = { initialScale },
|
||||||
targetWhenShowing = { 1f }
|
targetWhenShowing = { 1f }
|
||||||
)
|
)
|
||||||
|
|
||||||
val offset by animateDp(
|
val offset = animateDp(
|
||||||
targetWhenHiding = { from.offset },
|
targetWhenHiding = { initialOffset },
|
||||||
targetWhenShowing = { 0.dp }
|
targetWhenShowing = { 0.dp }
|
||||||
)
|
)
|
||||||
|
|
||||||
val alpha by animateFloat(
|
val alpha = animateFloat(
|
||||||
targetWhenHiding = { 0.2f },
|
targetWhenHiding = { 0.2f },
|
||||||
targetWhenShowing = { 0f }
|
targetWhenShowing = { 0f }
|
||||||
)
|
)
|
||||||
|
|
||||||
return AppScaffoldAnimationState(
|
return remember {
|
||||||
navigationState = AppScaffoldNavigator.NavigationState.RELEASE,
|
AppScaffoldAnimationState(
|
||||||
scale = scale,
|
scale = scale,
|
||||||
|
scaleMinimum = from.scaleMinimum,
|
||||||
offset = offset,
|
offset = offset,
|
||||||
parentModifier = Modifier.drawWithContent {
|
parentOverlayAlpha = alpha
|
||||||
drawContent()
|
|
||||||
|
|
||||||
drawRect(Color(0f, 0f, 0f, alpha))
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ThreePaneScaffoldPaneScope.defaultDetailInitAnimationState(): AppScaffoldAnimationState {
|
fun ThreePaneScaffoldPaneScope.defaultDetailInitAnimationState(): AppScaffoldAnimationState {
|
||||||
val offset by animateDp(
|
val offset = animateDp(
|
||||||
targetWhenHiding = {
|
targetWhenHiding = {
|
||||||
AppScaffoldAnimationDefaults.InitAnimationOffset
|
AppScaffoldAnimationDefaults.InitAnimationOffset
|
||||||
},
|
},
|
||||||
@@ -172,21 +169,22 @@ fun ThreePaneScaffoldPaneScope.defaultDetailInitAnimationState(): AppScaffoldAni
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
val alpha by animateFloat {
|
val alpha = animateFloat {
|
||||||
1f
|
1f
|
||||||
}
|
}
|
||||||
|
|
||||||
return AppScaffoldAnimationState(
|
return remember {
|
||||||
navigationState = AppScaffoldNavigator.NavigationState.ENTER,
|
AppScaffoldAnimationState(
|
||||||
alpha = alpha,
|
alpha = alpha,
|
||||||
offset = offset
|
offset = offset
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ThreePaneScaffoldPaneScope.defaultDetailSeekAnimationState(): AppScaffoldAnimationState {
|
fun ThreePaneScaffoldPaneScope.defaultDetailSeekAnimationState(): AppScaffoldAnimationState {
|
||||||
val scale by animateFloat(
|
val scale = animateFloat(
|
||||||
transitionSpec = {
|
transitionSpec = {
|
||||||
appScaffoldSeekSpring()
|
appScaffoldSeekSpring()
|
||||||
},
|
},
|
||||||
@@ -194,7 +192,7 @@ fun ThreePaneScaffoldPaneScope.defaultDetailSeekAnimationState(): AppScaffoldAni
|
|||||||
targetWhenHiding = { 0.5f }
|
targetWhenHiding = { 0.5f }
|
||||||
)
|
)
|
||||||
|
|
||||||
val offset by animateDp(
|
val offset = animateDp(
|
||||||
transitionSpec = {
|
transitionSpec = {
|
||||||
appScaffoldSeekSpring()
|
appScaffoldSeekSpring()
|
||||||
},
|
},
|
||||||
@@ -202,30 +200,44 @@ fun ThreePaneScaffoldPaneScope.defaultDetailSeekAnimationState(): AppScaffoldAni
|
|||||||
targetWhenHiding = { 88.dp }
|
targetWhenHiding = { 88.dp }
|
||||||
)
|
)
|
||||||
|
|
||||||
val roundedCorners by animateDp(
|
val roundedCorners = animateDp(
|
||||||
transitionSpec = {
|
transitionSpec = {
|
||||||
appScaffoldSeekSpring()
|
appScaffoldSeekSpring()
|
||||||
}
|
}
|
||||||
) { 1000.dp }
|
) { 1000.dp }
|
||||||
|
|
||||||
return AppScaffoldAnimationState(
|
return remember {
|
||||||
navigationState = AppScaffoldNavigator.NavigationState.SEEK,
|
AppScaffoldAnimationState(
|
||||||
scale = scale.coerceAtLeast(0.9f),
|
scale = scale,
|
||||||
|
scaleMinimum = 0.9f,
|
||||||
offset = offset,
|
offset = offset,
|
||||||
clipShape = RoundedCornerShape(roundedCorners.coerceAtMost(42.dp))
|
corners = roundedCorners,
|
||||||
|
cornersMaximum = 42.dp
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ThreePaneScaffoldPaneScope.defaultDetailReleaseAnimationState(from: AppScaffoldAnimationState): AppScaffoldAnimationState {
|
fun ThreePaneScaffoldPaneScope.defaultDetailReleaseAnimationState(from: AppScaffoldAnimationState): AppScaffoldAnimationState {
|
||||||
val alpha by animateFloat { 1f }
|
val scale = remember { from.contentScale }
|
||||||
|
val offset = remember { from.contentOffset }
|
||||||
|
val corners = remember { from.contentCorners }
|
||||||
|
|
||||||
return AppScaffoldAnimationState(
|
val scaleState = remember { mutableStateOf(scale) }
|
||||||
navigationState = AppScaffoldNavigator.NavigationState.RELEASE,
|
val offsetState = remember { mutableStateOf(offset) }
|
||||||
scale = from.scale,
|
val cornersState = remember { mutableStateOf(corners) }
|
||||||
offset = from.offset,
|
|
||||||
clipShape = from.clipShape,
|
val alpha = animateFloat { 1f }
|
||||||
|
|
||||||
|
return remember {
|
||||||
|
AppScaffoldAnimationState(
|
||||||
|
scale = scaleState,
|
||||||
|
scaleMinimum = from.scaleMinimum,
|
||||||
|
offset = offsetState,
|
||||||
|
corners = cornersState,
|
||||||
|
cornersMaximum = from.cornersMaximum,
|
||||||
alpha = alpha
|
alpha = alpha
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user