From 69e49b5d81acc867c9999aff66ba5d96ca2dea45 Mon Sep 17 00:00:00 2001 From: xfy Date: Fri, 15 May 2026 16:49:32 +0800 Subject: [PATCH] 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 --- .../plus/rua/project/CalendarViewModel.kt | 25 ++++++++++++++----- .../kotlin/plus/rua/project/ui/BottomCard.kt | 21 +++++++++++++--- .../plus/rua/project/ui/CalendarUtils.kt | 3 +++ 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/shared/src/commonMain/kotlin/plus/rua/project/CalendarViewModel.kt b/shared/src/commonMain/kotlin/plus/rua/project/CalendarViewModel.kt index da09596..3cc09e7 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/CalendarViewModel.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/CalendarViewModel.kt @@ -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) diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/BottomCard.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/BottomCard.kt index a1efeb3..848ff9e 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/BottomCard.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/BottomCard.kt @@ -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) } diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarUtils.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarUtils.kt index 509a534..aec9c04 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarUtils.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarUtils.kt @@ -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