PredictiveBack for ModalNavigationDrawer, SideDrawer

This commit is contained in:
skymkmk authored and Vladimir Dostoyevskiy committed 2023-11-24 00:47:36 +08:00
1 parent 6f6461e855
commit d69df5f7ab
3 files changed
+54 -11

No files matched your search

@@ -30,6 +30,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.positionChange
@@ -42,6 +43,8 @@ import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
import com.hippo.ehviewer.ui.tools.animateFloatMergeOneWayPredictiveBackAsState
import kotlin.math.roundToInt
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.launch
@@ -306,6 +309,16 @@ fun ModalNavigationDrawer(
},
color = scrimColor,
)
val predictiveState by animateFloatMergeOneWayPredictiveBackAsState(drawerState.isOpen) {
drawerState.close()
}
val predictiveModifier = if (drawerState.isOpen) {
Modifier.scale(lerp(1f, 0.9f, predictiveState)).offset {
IntOffset(lerp(0f, minValue * 0.05f, predictiveState).roundToInt(), 0)
}
} else {
Modifier
}
Box(
modifier = Modifier.offset {
IntOffset(drawerState.requireOffset().roundToInt(), 0)
@@ -314,7 +327,7 @@ fun ModalNavigationDrawer(
if (w != 0) {
minValue = -it.size.width.toFloat()
}
},
} then predictiveModifier,
) {
drawerContent()
}
@@ -398,6 +411,16 @@ fun SideDrawer(
},
color = scrimColor,
)
val predictiveState by animateFloatMergeOneWayPredictiveBackAsState(drawerState.isOpen) {
drawerState.close()
}
val predictiveModifier = if (drawerState.isOpen) {
Modifier.scale(lerp(1f, 0.9f, predictiveState)).offset {
IntOffset(lerp(0f, minValue * 0.05f, predictiveState).roundToInt(), 0)
}
} else {
Modifier
}
Box(
modifier = Modifier.offset {
IntOffset(drawerState.requireOffset().roundToInt(), 0)
@@ -406,7 +429,7 @@ fun SideDrawer(
if (w != 0) {
minValue = it.size.width.toFloat()
}
}.align(Alignment.TopEnd),
}.align(Alignment.TopEnd) then predictiveModifier,
) {
drawerContent()
}
@@ -26,7 +26,6 @@ import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
import androidx.compose.foundation.Image
@@ -300,9 +299,6 @@ class MainActivity : EhActivity() {
recomposeScope.invalidate()
}
}
BackHandler(drawerState.isOpen) {
closeDrawer()
}
LocalTouchSlopProvider(Settings.touchSlopFactor.toFloat()) {
ModalNavigationDrawer(
drawerContent = {
@@ -355,11 +351,6 @@ class MainActivity : EhActivity() {
sideDrawerState.open()
}
}
BackHandler(sideDrawerState.isOpen) {
scope.launch {
sideDrawerState.close()
}
}
SideDrawer(
drawerContent = {
if (sheet != null) {
@@ -14,6 +14,7 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
@@ -72,3 +73,31 @@ fun animateFloatMergePredictiveBackAsState(
}
return animatable.asState()
}
@Composable
fun animateFloatMergeOneWayPredictiveBackAsState(
enable: Boolean,
animationSpec: AnimationSpec<Float> = spring(),
predictiveBackInterpolator: Easing = EaseOut,
onBack: suspend () -> Unit,
): State<Float> {
val animatable = remember { Animatable(0f, Float.VectorConverter) }
PredictiveBackHandler(enable) { progress ->
try {
progress.collect {
val transformed = predictiveBackInterpolator.transform(it.progress)
animatable.snapTo(transformed)
}
} catch (e: CancellationException) {
withNonCancellableContext {
animatable.animateTo(0f, animationSpec)
}
throw e
}
withNonCancellableContext {
onBack()
animatable.snapTo(0f)
}
}
return animatable.asState()
}