Add fling velocity threshold to collapse/expand drag gesture
Quick swipe now snaps to the target state regardless of progress position, matching the behavior of HorizontalPager's fling logic. Uses VelocityTracker to measure release velocity and a 800 dp/s threshold (FLING_VELOCITY_THRESHOLD_DP). Slow drags still use the positional COLLAPSE_THRESHOLD (50%) as before. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
d0492d02f0
commit
69e49b5d81
@ -17,6 +17,7 @@ import kotlinx.datetime.plus
|
||||
import kotlinx.datetime.todayIn
|
||||
import kotlin.time.Clock
|
||||
import plus.rua.project.ui.COLLAPSE_THRESHOLD
|
||||
import plus.rua.project.ui.FLING_VELOCITY_THRESHOLD_DP
|
||||
|
||||
data class CalendarDay(
|
||||
val date: LocalDate,
|
||||
@ -61,10 +62,16 @@ class CalendarViewModel(
|
||||
}
|
||||
|
||||
// 拖拽超过阈值时自动折叠到周视图,否则回弹到月视图
|
||||
fun onDragEnd() {
|
||||
// velocityDpPerSec: 松手时的 fling 速度 (dp/s),正值=上滑(折叠方向),负值=下滑(展开方向)
|
||||
fun onDragEnd(velocityDpPerSec: Float = 0f) {
|
||||
coroutineScope.launch {
|
||||
val current = _collapseAnimatable.value
|
||||
if (current > COLLAPSE_THRESHOLD) {
|
||||
val progress = _collapseAnimatable.value
|
||||
val shouldCollapse = when {
|
||||
velocityDpPerSec > FLING_VELOCITY_THRESHOLD_DP -> true // 快速上滑→折叠
|
||||
velocityDpPerSec < -FLING_VELOCITY_THRESHOLD_DP -> false // 快速下滑→展开
|
||||
else -> progress > COLLAPSE_THRESHOLD // 慢速按 progress 判断
|
||||
}
|
||||
if (shouldCollapse) {
|
||||
_collapseAnimatable.animateTo(
|
||||
targetValue = 1f,
|
||||
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)
|
||||
@ -88,10 +95,16 @@ class CalendarViewModel(
|
||||
}
|
||||
|
||||
// 下拉超过阈值时自动展开到月视图,否则回弹到周视图
|
||||
fun onExpandDragEnd() {
|
||||
// velocityDpPerSec: 同上,正值=上滑,负值=下滑
|
||||
fun onExpandDragEnd(velocityDpPerSec: Float = 0f) {
|
||||
coroutineScope.launch {
|
||||
val current = _collapseAnimatable.value
|
||||
if (current < COLLAPSE_THRESHOLD) {
|
||||
val progress = _collapseAnimatable.value
|
||||
val shouldExpand = when {
|
||||
velocityDpPerSec < -FLING_VELOCITY_THRESHOLD_DP -> true // 快速下滑→展开
|
||||
velocityDpPerSec > FLING_VELOCITY_THRESHOLD_DP -> false // 快速上滑→保持折叠
|
||||
else -> progress < COLLAPSE_THRESHOLD // 慢速按 progress 判断
|
||||
}
|
||||
if (shouldExpand) {
|
||||
_collapseAnimatable.animateTo(
|
||||
targetValue = 0f,
|
||||
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)
|
||||
|
||||
@ -11,11 +11,13 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import plus.rua.project.CalendarViewModel
|
||||
|
||||
@ -33,20 +35,27 @@ fun BottomCard(
|
||||
dragRangePx: Float,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val density = LocalDensity.current
|
||||
|
||||
Surface(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.pointerInput(viewModel.isCollapsed) {
|
||||
val velocityTracker = androidx.compose.ui.input.pointer.util.VelocityTracker()
|
||||
if (viewModel.isCollapsed) {
|
||||
// 折叠状态:下拉恢复到月视图
|
||||
detectVerticalDragGestures(
|
||||
onDragEnd = {
|
||||
viewModel.onExpandDragEnd()
|
||||
val velocity = velocityTracker.calculateVelocity()
|
||||
// 上滑为正(折叠方向),下拉为负(展开方向)
|
||||
val velocityDpPerSec = with(density) { -velocity.y.toDp().value }
|
||||
viewModel.onExpandDragEnd(velocityDpPerSec)
|
||||
},
|
||||
onDragCancel = {
|
||||
viewModel.onExpandDragEnd()
|
||||
}
|
||||
) { _, dragAmount ->
|
||||
) { change, dragAmount ->
|
||||
velocityTracker.addPosition(change.uptimeMillis, change.position)
|
||||
val delta = -dragAmount / dragRangePx
|
||||
viewModel.onExpandDrag(delta)
|
||||
}
|
||||
@ -54,12 +63,16 @@ fun BottomCard(
|
||||
// 展开状态:上拉折叠到周视图
|
||||
detectVerticalDragGestures(
|
||||
onDragEnd = {
|
||||
viewModel.onDragEnd()
|
||||
val velocity = velocityTracker.calculateVelocity()
|
||||
// 上滑为正(折叠方向),下拉为负(展开方向)
|
||||
val velocityDpPerSec = with(density) { -velocity.y.toDp().value }
|
||||
viewModel.onDragEnd(velocityDpPerSec)
|
||||
},
|
||||
onDragCancel = {
|
||||
viewModel.onDragEnd()
|
||||
}
|
||||
) { _, dragAmount ->
|
||||
) { change, dragAmount ->
|
||||
velocityTracker.addPosition(change.uptimeMillis, change.position)
|
||||
val delta = -dragAmount / dragRangePx
|
||||
viewModel.onDrag(delta)
|
||||
}
|
||||
|
||||
@ -24,6 +24,9 @@ const val HORIZONTAL_PADDING_DP = 16
|
||||
/** BottomCard 拖拽手势范围最小值 (dp),防止行数少时 dragRange 过小 */
|
||||
const val DRAG_RANGE_MIN_DP = 100
|
||||
|
||||
/** fling 速度阈值 (dp/s),超过此速度按方向直接折叠/展开,不受 progress 阈值限制 */
|
||||
const val FLING_VELOCITY_THRESHOLD_DP = 800
|
||||
|
||||
/** 日历与 BottomCard 之间的间距 (dp):展开时 */
|
||||
const val CARD_GAP_EXPANDED_DP = 24
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user