Add state and snapTo method

This commit is contained in:
Oleksandr Balan
2022-06-30 00:30:22 +02:00
parent b00345dd86
commit 8495cbf168
5 changed files with 260 additions and 87 deletions

View File

@@ -16,21 +16,18 @@ public data class PageCurlConfig(
@ExperimentalPageCurlApi
public data class InteractionConfig(
val forward: CurlDirection.Forward = CurlDirection.Forward(
val forward: CurlDirection = CurlDirection(
Rect(Offset(0.5f, 0.0f), Offset(1.0f, 1.0f)),
Rect(Offset(0.0f, 0.0f), Offset(0.5f, 1.0f)),
),
val backward: CurlDirection.Backward = CurlDirection.Backward(forward.end, forward.start),
val backward: CurlDirection = CurlDirection(forward.end, forward.start),
)
@ExperimentalPageCurlApi
public sealed interface CurlDirection {
public val start: Rect
public val end: Rect
public data class Forward(override val start: Rect, override val end: Rect) : CurlDirection
public data class Backward(override val start: Rect, override val end: Rect) : CurlDirection
}
public data class CurlDirection(
val start: Rect,
val end: Rect
)
@ExperimentalPageCurlApi
public data class CurlConfig(

View File

@@ -19,13 +19,14 @@ import kotlin.math.PI
@ExperimentalPageCurlApi
internal fun Modifier.curlGesture(
key: Any?,
enabled: Boolean,
direction: CurlDirection,
onStart: () -> Unit,
onCurl: (Offset, Offset) -> Unit,
onEnd: () -> Unit,
onCancel: () -> Unit,
): Modifier = pointerInput(enabled) {
): Modifier = pointerInput(enabled, key) {
if (!enabled) {
return@pointerInput
}
@@ -53,6 +54,11 @@ internal fun Modifier.curlGesture(
onCurl(dragCurrent - vector, dragCurrent + vector)
}
if (dragCurrent == dragStart) {
onCancel()
return@awaitPointerEventScope
}
val velocity = velocityTracker.calculateVelocity()
val decay = splineBasedDecay<Offset>(this)
val target = decay.calculateTargetValue(

View File

@@ -8,11 +8,15 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.Constraints
import eu.wewox.pagecurl.ExperimentalPageCurlApi
import eu.wewox.pagecurl.config.CurlDirection
import eu.wewox.pagecurl.config.PageCurlConfig
@@ -22,58 +26,54 @@ import kotlinx.coroutines.launch
@ExperimentalPageCurlApi
@Composable
public fun PageCurl(
current: Int,
count: Int,
state: PageCurlState,
modifier: Modifier = Modifier,
onCurrentChange: (Int) -> Unit,
config: PageCurlConfig = PageCurlConfig(),
content: @Composable (Int) -> Unit
) {
val scope = rememberCoroutineScope()
val updatedCurrent by rememberUpdatedState(current)
val updatedCurrent by rememberUpdatedState(state.current)
BoxWithConstraints(modifier) {
val maxWidthPx = constraints.maxWidth.toFloat()
val maxHeightPx = constraints.maxHeight.toFloat()
val left = Curl(Offset(0f, 0f), Offset(0f, maxHeightPx))
val right = Curl(Offset(maxWidthPx, 0f), Offset(maxWidthPx, maxHeightPx))
state.setup(constraints)
val forward = remember { Animatable(right, Curl.VectorConverter, Curl.VisibilityThreshold) }
val backward = remember { Animatable(left, Curl.VectorConverter, Curl.VisibilityThreshold) }
val internalState by rememberUpdatedState(state.internalState ?: return@BoxWithConstraints)
Box(
Modifier
.curlGesture(
enabled = updatedCurrent < count - 1,
key = internalState,
enabled = updatedCurrent < state.max - 1,
scope = scope,
direction = config.interaction.forward,
start = right,
end = left,
animatable = forward,
onChange = { onCurrentChange(updatedCurrent + 1) }
start = internalState.rightCurl,
end = internalState.leftCurl,
animatable = internalState.forward,
onChange = { state.current = updatedCurrent + 1 }
)
.curlGesture(
key = internalState,
enabled = updatedCurrent > 0,
scope = scope,
direction = config.interaction.backward,
start = left,
end = right,
animatable = backward,
onChange = { onCurrentChange(updatedCurrent - 1) }
start = internalState.leftCurl,
end = internalState.rightCurl,
animatable = internalState.backward,
onChange = { state.current = updatedCurrent - 1 }
)
) {
if (updatedCurrent + 1 < count) {
if (updatedCurrent + 1 < state.max) {
content(updatedCurrent + 1)
}
if (updatedCurrent < count) {
Box(Modifier.drawCurl(config.curl, forward.value.a, forward.value.b)) {
if (updatedCurrent < state.max) {
Box(Modifier.drawCurl(config.curl, internalState.forward.value.a, internalState.forward.value.b)) {
content(updatedCurrent)
}
}
if (updatedCurrent > 0) {
Box(Modifier.drawCurl(config.curl, backward.value.a, backward.value.b)) {
Box(Modifier.drawCurl(config.curl, internalState.backward.value.a, internalState.backward.value.b)) {
content(updatedCurrent - 1)
}
}
@@ -81,8 +81,79 @@ public fun PageCurl(
}
}
@ExperimentalPageCurlApi
@Composable
public fun rememberPageCurlState(
max: Int,
initialCurrent: Int = 0,
): PageCurlState =
rememberSaveable(
max, initialCurrent,
saver = Saver(
save = { it.current },
restore = {
PageCurlState(
max = it,
initialCurrent = initialCurrent
)
}
)
) {
PageCurlState(
max = max,
initialCurrent = initialCurrent
)
}
@ExperimentalPageCurlApi
public class PageCurlState(
public val max: Int,
initialCurrent: Int = 0,
) {
public var current: Int by mutableStateOf(initialCurrent)
internal set
internal var internalState: InternalState? by mutableStateOf(null)
internal fun setup(constraints: Constraints) {
if (internalState?.constraints == constraints) {
return
}
val maxWidthPx = constraints.maxWidth.toFloat()
val maxHeightPx = constraints.maxHeight.toFloat()
val left = Curl(Offset(0f, 0f), Offset(0f, maxHeightPx))
val right = Curl(Offset(maxWidthPx, 0f), Offset(maxWidthPx, maxHeightPx))
val forward = Animatable(right, Curl.VectorConverter, Curl.VisibilityThreshold)
val backward = Animatable(left, Curl.VectorConverter, Curl.VisibilityThreshold)
internalState = InternalState(constraints, left, right, forward, backward)
}
public suspend fun snapTo(value: Int) {
internalState?.reset()
current = value
}
internal data class InternalState(
val constraints: Constraints,
val leftCurl: Curl,
val rightCurl: Curl,
val forward: Animatable<Curl, AnimationVector4D>,
val backward: Animatable<Curl, AnimationVector4D>,
) {
internal suspend fun reset() {
forward.snapTo(rightCurl)
backward.snapTo(leftCurl)
}
}
}
@ExperimentalPageCurlApi
private fun Modifier.curlGesture(
key: Any?,
enabled: Boolean,
scope: CoroutineScope,
direction: CurlDirection,
@@ -92,6 +163,7 @@ private fun Modifier.curlGesture(
onChange: () -> Unit,
): Modifier =
curlGesture(
key = key,
enabled = enabled,
direction = direction,
onStart = {
@@ -121,8 +193,7 @@ private fun Modifier.curlGesture(
},
)
private data class Curl(val a: Offset, val b: Offset) {
internal data class Curl(val a: Offset, val b: Offset) {
companion object {
val VectorConverter: TwoWayConverter<Curl, AnimationVector4D> =
TwoWayConverter(