Fix scroll animation when system animation is disabled (#7509)

This commit is contained in:
Ivan Iskandar 2022-07-12 09:21:00 +07:00 committed by GitHub
parent 788583e66f
commit ba93060e59
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 97 additions and 7 deletions

View file

@ -3,7 +3,6 @@ package eu.kanade.presentation.browse
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -15,6 +14,7 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.LazyColumn
import eu.kanade.presentation.components.LoadingScreen import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.components.PreferenceRow import eu.kanade.presentation.components.PreferenceRow
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R

View file

@ -2,12 +2,12 @@ package eu.kanade.presentation.category.components
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.domain.category.model.Category import eu.kanade.domain.category.model.Category
import eu.kanade.presentation.components.LazyColumn
@Composable @Composable
fun CategoryContent( fun CategoryContent(

View file

@ -1,11 +1,9 @@
package eu.kanade.presentation.components package eu.kanade.presentation.components
import androidx.compose.foundation.gestures.FlingBehavior import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
@ -17,6 +15,38 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.presentation.util.drawVerticalScrollbar import eu.kanade.presentation.util.drawVerticalScrollbar
import eu.kanade.presentation.util.flingBehaviorIgnoringMotionScale
/**
* LazyColumn with fling animation fix
*
* @see flingBehaviorIgnoringMotionScale
*/
@Composable
fun LazyColumn(
modifier: Modifier = Modifier,
state: LazyListState = rememberLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(),
userScrollEnabled: Boolean = true,
content: LazyListScope.() -> Unit,
) {
androidx.compose.foundation.lazy.LazyColumn(
modifier = modifier,
state = state,
contentPadding = contentPadding,
reverseLayout = reverseLayout,
verticalArrangement = verticalArrangement,
horizontalAlignment = horizontalAlignment,
flingBehavior = flingBehavior,
userScrollEnabled = userScrollEnabled,
content = content,
)
}
/** /**
* LazyColumn with scrollbar. * LazyColumn with scrollbar.
@ -30,7 +60,7 @@ fun ScrollbarLazyColumn(
verticalArrangement: Arrangement.Vertical = verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal = Alignment.Start, horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(),
userScrollEnabled: Boolean = true, userScrollEnabled: Boolean = true,
content: LazyListScope.() -> Unit, content: LazyListScope.() -> Unit,
) { ) {
@ -69,7 +99,7 @@ fun FastScrollLazyColumn(
verticalArrangement: Arrangement.Vertical = verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal = Alignment.Start, horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(),
userScrollEnabled: Boolean = true, userScrollEnabled: Boolean = true,
content: LazyListScope.() -> Unit, content: LazyListScope.() -> Unit,
) { ) {

View file

@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
@ -52,6 +51,7 @@ import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import eu.kanade.domain.chapter.model.Chapter import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.presentation.components.ExtendedFloatingActionButton import eu.kanade.presentation.components.ExtendedFloatingActionButton
import eu.kanade.presentation.components.LazyColumn
import eu.kanade.presentation.components.Scaffold import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.components.SwipeRefreshIndicator import eu.kanade.presentation.components.SwipeRefreshIndicator
import eu.kanade.presentation.components.VerticalFastScroller import eu.kanade.presentation.components.VerticalFastScroller

View file

@ -0,0 +1,60 @@
package eu.kanade.presentation.util
import androidx.compose.animation.core.AnimationState
import androidx.compose.animation.core.DecayAnimationSpec
import androidx.compose.animation.core.animateDecay
import androidx.compose.animation.rememberSplineBasedDecay
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.ScrollScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.MotionDurationScale
import kotlinx.coroutines.withContext
import kotlin.math.abs
/**
* FlingBehavior that always uses the default motion scale.
*
* This makes the scrolling animation works like View's lists
* when "Remove animation" settings is on.
*/
@Composable
fun flingBehaviorIgnoringMotionScale(): FlingBehavior {
val flingSpec = rememberSplineBasedDecay<Float>()
return remember(flingSpec) {
DefaultFlingBehavior(flingSpec)
}
}
private val DefaultMotionDurationScale = object : MotionDurationScale {
// Use default motion scale factor
override val scaleFactor: Float = 1f
}
private class DefaultFlingBehavior(
private val flingDecay: DecayAnimationSpec<Float>,
) : FlingBehavior {
override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
// come up with the better threshold, but we need it since spline curve gives us NaNs
return if (abs(initialVelocity) > 1f) {
var velocityLeft = initialVelocity
var lastValue = 0f
withContext(DefaultMotionDurationScale) {
AnimationState(
initialValue = 0f,
initialVelocity = initialVelocity,
).animateDecay(flingDecay) {
val delta = value - lastValue
val consumed = scrollBy(delta)
lastValue = value
velocityLeft = this.velocity
// avoid rounding errors and stop if anything is unconsumed
if (abs(delta - consumed) > 0.5f) this.cancelAnimation()
}
}
velocityLeft
} else {
initialVelocity
}
}
}