mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 02:34:39 +00:00
fix(AppBar): Re-introduce snap but only do it to the top bar
This commit is contained in:
parent
448c93365a
commit
dec1a70091
2 changed files with 32 additions and 24 deletions
|
@ -19,7 +19,8 @@ The format is simplified version of [Keep a Changelog](https://keepachangelog.co
|
||||||
- Refactor Library to utilize Flow even more
|
- Refactor Library to utilize Flow even more
|
||||||
- Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.10.1
|
- Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.10.1
|
||||||
- Refactor EmptyView to use Compose
|
- Refactor EmptyView to use Compose
|
||||||
- Refactor Reader ChapterTransition to use Compose
|
- Refactor Reader ChapterTransition to use Compose (@arkon)
|
||||||
|
- [Experimental] Add modified version of LargeTopAppBar that mimic J2K's ExpandedAppBarLayout
|
||||||
|
|
||||||
## [1.9.7]
|
## [1.9.7]
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,10 @@ import androidx.compose.animation.core.AnimationState
|
||||||
import androidx.compose.animation.core.CubicBezierEasing
|
import androidx.compose.animation.core.CubicBezierEasing
|
||||||
import androidx.compose.animation.core.DecayAnimationSpec
|
import androidx.compose.animation.core.DecayAnimationSpec
|
||||||
import androidx.compose.animation.core.FastOutLinearInEasing
|
import androidx.compose.animation.core.FastOutLinearInEasing
|
||||||
|
import androidx.compose.animation.core.Spring
|
||||||
import androidx.compose.animation.core.animateDecay
|
import androidx.compose.animation.core.animateDecay
|
||||||
import androidx.compose.animation.core.animateTo
|
import androidx.compose.animation.core.animateTo
|
||||||
|
import androidx.compose.animation.core.spring
|
||||||
import androidx.compose.animation.rememberSplineBasedDecay
|
import androidx.compose.animation.rememberSplineBasedDecay
|
||||||
import androidx.compose.foundation.gestures.Orientation
|
import androidx.compose.foundation.gestures.Orientation
|
||||||
import androidx.compose.foundation.gestures.draggable
|
import androidx.compose.foundation.gestures.draggable
|
||||||
|
@ -141,9 +143,7 @@ private fun TwoRowsTopAppBar(
|
||||||
// collapse.
|
// collapse.
|
||||||
// This will potentially animate or interpolate a transition between the container color and the
|
// This will potentially animate or interpolate a transition between the container color and the
|
||||||
// container's scrolled color according to the app bar's scroll state.
|
// container's scrolled color according to the app bar's scroll state.
|
||||||
val colorTransitionFraction = scrollBehavior?.state?.collapsedFraction ?: 0f
|
val colorTransitionFraction = scrollBehavior?.state?.bottomCollapsedFraction(collapsedHeightPx, expandedHeightPx) ?: 0f
|
||||||
val topColorTransitionFraction = scrollBehavior?.state?.topCollapsedFraction(collapsedHeightPx) ?: 0f
|
|
||||||
val bottomColorTransitionFraction = scrollBehavior?.state?.bottomCollapsedFraction(collapsedHeightPx, expandedHeightPx) ?: 0f
|
|
||||||
|
|
||||||
val appBarContainerColor =
|
val appBarContainerColor =
|
||||||
lerp(
|
lerp(
|
||||||
|
@ -161,12 +161,12 @@ private fun TwoRowsTopAppBar(
|
||||||
content = actions
|
content = actions
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val topTitleAlpha = TopTitleAlphaEasing.transform(topColorTransitionFraction)
|
val topTitleAlpha = TitleAlphaEasing.transform(colorTransitionFraction)
|
||||||
val bottomTitleAlpha = 1f - bottomColorTransitionFraction
|
val bottomTitleAlpha = 1f - colorTransitionFraction
|
||||||
// Hide the top row title semantics when its alpha value goes below 0.5 threshold.
|
// Hide the top row title semantics when its alpha value goes below 0.5 threshold.
|
||||||
// Hide the bottom row title semantics when the top title semantics are active.
|
// Hide the bottom row title semantics when the top title semantics are active.
|
||||||
val hideTopRowSemantics = topColorTransitionFraction < 0.5f
|
val hideTopRowSemantics = colorTransitionFraction < 0.5f
|
||||||
val hideBottomRowSemantics = bottomColorTransitionFraction < 0.5f
|
val hideBottomRowSemantics = !hideTopRowSemantics
|
||||||
|
|
||||||
// Set up support for resizing the top app bar when vertically dragging the bar itself.
|
// Set up support for resizing the top app bar when vertically dragging the bar itself.
|
||||||
val appBarDragModifier =
|
val appBarDragModifier =
|
||||||
|
@ -179,6 +179,8 @@ private fun TwoRowsTopAppBar(
|
||||||
settleAppBar(
|
settleAppBar(
|
||||||
scrollBehavior.state,
|
scrollBehavior.state,
|
||||||
velocity,
|
velocity,
|
||||||
|
collapsedHeightPx,
|
||||||
|
expandedHeightPx,
|
||||||
scrollBehavior.flingAnimationSpec,
|
scrollBehavior.flingAnimationSpec,
|
||||||
scrollBehavior.snapAnimationSpec
|
scrollBehavior.snapAnimationSpec
|
||||||
)
|
)
|
||||||
|
@ -461,14 +463,16 @@ private fun interface ScrolledOffset {
|
||||||
private suspend fun settleAppBar(
|
private suspend fun settleAppBar(
|
||||||
state: TopAppBarState,
|
state: TopAppBarState,
|
||||||
velocity: Float,
|
velocity: Float,
|
||||||
|
topHeightPx: Float,
|
||||||
|
totalHeightPx: Float,
|
||||||
flingAnimationSpec: DecayAnimationSpec<Float>?,
|
flingAnimationSpec: DecayAnimationSpec<Float>?,
|
||||||
snapAnimationSpec: AnimationSpec<Float>?
|
snapAnimationSpec: AnimationSpec<Float>?,
|
||||||
): Velocity {
|
): Velocity {
|
||||||
// Check if the app bar is completely collapsed/expanded. If so, no need to settle the app bar,
|
// Check if the app bar is completely collapsed/expanded. If so, no need to settle the app bar,
|
||||||
// and just return Zero Velocity.
|
// and just return Zero Velocity.
|
||||||
// Note that we don't check for 0f due to float precision with the collapsedFraction
|
// Note that we don't check for 0f due to float precision with the collapsedFraction
|
||||||
// calculation.
|
// calculation.
|
||||||
if (state.collapsedFraction < 0.01f || state.collapsedFraction == 1f) {
|
if (state.topCollapsedFraction(topHeightPx, totalHeightPx) < 0.01f || state.topCollapsedFraction(topHeightPx, totalHeightPx) == 1f) {
|
||||||
return Velocity.Zero
|
return Velocity.Zero
|
||||||
}
|
}
|
||||||
var remainingVelocity = velocity
|
var remainingVelocity = velocity
|
||||||
|
@ -493,17 +497,16 @@ private suspend fun settleAppBar(
|
||||||
}
|
}
|
||||||
// Snap if animation specs were provided.
|
// Snap if animation specs were provided.
|
||||||
if (snapAnimationSpec != null) {
|
if (snapAnimationSpec != null) {
|
||||||
// FIXME: Only snap the top app bar
|
if (state.topHeightOffset(topHeightPx, totalHeightPx) < 0 && state.topHeightOffset(topHeightPx, totalHeightPx) > -topHeightPx) {
|
||||||
if (state.heightOffset < 0 && state.heightOffset > state.heightOffsetLimit) {
|
AnimationState(initialValue = state.topHeightOffset(topHeightPx, totalHeightPx)).animateTo(
|
||||||
AnimationState(initialValue = state.heightOffset).animateTo(
|
if (state.topCollapsedFraction(topHeightPx, totalHeightPx) < 0.5f) {
|
||||||
if (state.collapsedFraction < 0.5f) {
|
|
||||||
0f
|
0f
|
||||||
} else {
|
} else {
|
||||||
state.heightOffsetLimit
|
-topHeightPx
|
||||||
},
|
},
|
||||||
animationSpec = snapAnimationSpec
|
animationSpec = snapAnimationSpec
|
||||||
) {
|
) {
|
||||||
state.heightOffset = value
|
state.heightOffset = value + (topHeightPx - totalHeightPx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -520,18 +523,21 @@ private fun TopAppBarState.bottomHeightOffset(topHeightPx: Float, totalHeightPx:
|
||||||
return heightOffset.coerceIn(topHeightPx - totalHeightPx, 0f)
|
return heightOffset.coerceIn(topHeightPx - totalHeightPx, 0f)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TopAppBarState.topCollapsedFraction(topHeightPx: Float): Float {
|
private fun TopAppBarState.topCollapsedFraction(topHeightPx: Float, totalHeightPx: Float): Float {
|
||||||
return heightOffset / -topHeightPx
|
val offset = topHeightOffset(topHeightPx, totalHeightPx)
|
||||||
|
return offset / -topHeightPx
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TopAppBarState.bottomCollapsedFraction(topHeightPx: Float, totalHeightPx: Float): Float {
|
private fun TopAppBarState.bottomCollapsedFraction(topHeightPx: Float, totalHeightPx: Float): Float {
|
||||||
return heightOffset / (topHeightPx - totalHeightPx)
|
val offset = bottomHeightOffset(topHeightPx, totalHeightPx)
|
||||||
|
return offset / (topHeightPx - totalHeightPx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun enterAlwaysCollapsedScrollBehavior(
|
fun enterAlwaysCollapsedScrollBehavior(
|
||||||
state: TopAppBarState = rememberTopAppBarState(),
|
state: TopAppBarState = rememberTopAppBarState(),
|
||||||
canScroll: () -> Boolean = { true },
|
canScroll: () -> Boolean = { true },
|
||||||
|
snapAnimationSpec: AnimationSpec<Float>? = spring(stiffness = Spring.StiffnessMediumLow),
|
||||||
flingAnimationSpec: DecayAnimationSpec<Float>? = rememberSplineBasedDecay()
|
flingAnimationSpec: DecayAnimationSpec<Float>? = rememberSplineBasedDecay()
|
||||||
): TopAppBarScrollBehavior {
|
): TopAppBarScrollBehavior {
|
||||||
val topHeightPx: Float
|
val topHeightPx: Float
|
||||||
|
@ -543,6 +549,7 @@ fun enterAlwaysCollapsedScrollBehavior(
|
||||||
|
|
||||||
return EnterAlwaysCollapsedScrollBehavior(
|
return EnterAlwaysCollapsedScrollBehavior(
|
||||||
state = state,
|
state = state,
|
||||||
|
snapAnimationSpec = snapAnimationSpec,
|
||||||
flingAnimationSpec = flingAnimationSpec,
|
flingAnimationSpec = flingAnimationSpec,
|
||||||
canScroll = canScroll,
|
canScroll = canScroll,
|
||||||
topHeightPx = topHeightPx,
|
topHeightPx = topHeightPx,
|
||||||
|
@ -550,21 +557,21 @@ fun enterAlwaysCollapsedScrollBehavior(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Current it behaves exactly like EnterAlways
|
|
||||||
private class EnterAlwaysCollapsedScrollBehavior(
|
private class EnterAlwaysCollapsedScrollBehavior(
|
||||||
override val state: TopAppBarState,
|
override val state: TopAppBarState,
|
||||||
|
override val snapAnimationSpec: AnimationSpec<Float>?,
|
||||||
override val flingAnimationSpec: DecayAnimationSpec<Float>?,
|
override val flingAnimationSpec: DecayAnimationSpec<Float>?,
|
||||||
val canScroll: () -> Boolean = { true },
|
val canScroll: () -> Boolean = { true },
|
||||||
val topHeightPx: Float,
|
val topHeightPx: Float,
|
||||||
val totalHeightPx: Float,
|
val totalHeightPx: Float,
|
||||||
) : TopAppBarScrollBehavior {
|
) : TopAppBarScrollBehavior {
|
||||||
override val snapAnimationSpec: AnimationSpec<Float>? = null // J2K's app bar doesn't do snap
|
|
||||||
override val isPinned: Boolean = false
|
override val isPinned: Boolean = false
|
||||||
override var nestedScrollConnection =
|
override var nestedScrollConnection =
|
||||||
object : NestedScrollConnection {
|
object : NestedScrollConnection {
|
||||||
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
|
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
|
||||||
// Don't intercept if scrolling down.
|
// Don't intercept if scrolling down.
|
||||||
if (!canScroll()) return Offset.Zero
|
if (!canScroll() || (available.y > 0f && state.topHeightOffset(topHeightPx, totalHeightPx) >= 0f))
|
||||||
|
return Offset.Zero
|
||||||
|
|
||||||
val prevHeightOffset = state.heightOffset
|
val prevHeightOffset = state.heightOffset
|
||||||
state.heightOffset += available.y
|
state.heightOffset += available.y
|
||||||
|
@ -611,14 +618,14 @@ private class EnterAlwaysCollapsedScrollBehavior(
|
||||||
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
|
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
|
||||||
val superConsumed = super.onPostFling(consumed, available)
|
val superConsumed = super.onPostFling(consumed, available)
|
||||||
return superConsumed +
|
return superConsumed +
|
||||||
settleAppBar(state, available.y, flingAnimationSpec, snapAnimationSpec)
|
settleAppBar(state, available.y, topHeightPx, totalHeightPx, flingAnimationSpec, snapAnimationSpec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val CollapsedContainerHeight = 64.0.dp
|
val CollapsedContainerHeight = 64.0.dp
|
||||||
val ExpandedContainerHeight = 152.0.dp
|
val ExpandedContainerHeight = 152.0.dp
|
||||||
internal val TopTitleAlphaEasing = CubicBezierEasing(.8f, 0f, .8f, .15f)
|
internal val TitleAlphaEasing = CubicBezierEasing(.8f, 0f, .8f, .15f)
|
||||||
private val MediumTitleBottomPadding = 24.dp
|
private val MediumTitleBottomPadding = 24.dp
|
||||||
private val LargeTitleBottomPadding = 28.dp
|
private val LargeTitleBottomPadding = 28.dp
|
||||||
private val TopAppBarHorizontalPadding = 4.dp
|
private val TopAppBarHorizontalPadding = 4.dp
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue