Fix swipe interpolation discontinuity and remove debug println

Use currentPage instead of settledPage for interpolatedWeeks calculation
to prevent gridH jumps during month transitions. When offsetFraction is
near zero, compute weeks from currentPage rather than stale callback
state. Remove all debug println statements and TAG constants.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
xfy 2026-05-15 10:08:25 +08:00
parent fbb7904880
commit ce6da44c52
4 changed files with 30 additions and 29 deletions

View File

@ -59,7 +59,8 @@ class CalendarViewModel(private val coroutineScope: CoroutineScope) {
// 拖拽超过 50% 时自动折叠到周视图,否则回弹到月视图
fun onDragEnd() {
coroutineScope.launch {
if (_collapseAnimatable.value > 0.5f) {
val current = _collapseAnimatable.value
if (current > 0.5f) {
_collapseAnimatable.animateTo(
targetValue = 1f,
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)
@ -85,7 +86,8 @@ class CalendarViewModel(private val coroutineScope: CoroutineScope) {
// 下拉超过 50% 时自动展开到月视图,否则回弹到周视图
fun onExpandDragEnd() {
coroutineScope.launch {
if (_collapseAnimatable.value < 0.5f) {
val current = _collapseAnimatable.value
if (current < 0.5f) {
_collapseAnimatable.animateTo(
targetValue = 0f,
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)

View File

@ -41,8 +41,12 @@ fun BottomCard(
if (viewModel.isCollapsed) {
// 折叠状态:下拉恢复到月视图
detectVerticalDragGestures(
onDragEnd = { viewModel.onExpandDragEnd() },
onDragCancel = { viewModel.onExpandDragEnd() }
onDragEnd = {
viewModel.onExpandDragEnd()
},
onDragCancel = {
viewModel.onExpandDragEnd()
}
) { _, dragAmount ->
val delta = -dragAmount / dragRange
viewModel.onExpandDrag(delta)
@ -50,8 +54,12 @@ fun BottomCard(
} else {
// 展开状态:上拉折叠到周视图
detectVerticalDragGestures(
onDragEnd = { viewModel.onDragEnd() },
onDragCancel = { viewModel.onDragEnd() }
onDragEnd = {
viewModel.onDragEnd()
},
onDragCancel = {
viewModel.onDragEnd()
}
) { _, dragAmount ->
val delta = -dragAmount / dragRange
viewModel.onDrag(delta)

View File

@ -19,8 +19,6 @@ import kotlinx.datetime.LocalDate
import kotlinx.datetime.minus
import kotlinx.datetime.plus
private const val TAG = "CalMonthPage"
/**
* 月度日历网格页面支持折叠动画
*
@ -59,11 +57,8 @@ fun CalendarMonthPage(
val totalHeightDp = if (rowHeightPx > 0) {
val p = collapseProgress
val totalPx = H * (1 + (effectiveWeeks - 1) * (1f - p))
println("[$TAG] year=$year month=$month rowH=$rowHeightPx H=$H effWeeks=$effectiveWeeks " +
"weeks.size=${weeks.size} p=$p totalPx=$totalPx selWeek=$selectedWeekIndex")
with(density) { totalPx.toDp() }
} else {
println("[$TAG] year=$year month=$month rowH=0 (not yet measured)")
null
}
@ -98,7 +93,8 @@ fun CalendarMonthPage(
}
with(density) { yPx.toDp() }
} else if (rowHeightPx > 0) {
with(density) { (weekIndex * H).toDp() }
val yPx = weekIndex * H
with(density) { yPx.toDp() }
} else {
0.dp
}

View File

@ -32,7 +32,6 @@ import plus.rua.project.CalendarViewModel
private const val START_PAGE = Int.MAX_VALUE / 2
private const val ROW_PADDING_DP = 4
private const val TAG = "CalMonthView"
/**
* 日历主界面包含月/周视图切换和折叠动画
@ -68,21 +67,23 @@ fun CalendarMonthView(
val rowPaddingPx = with(density) { ROW_PADDING_DP.dp.toPx() }.toInt()
// 滑动偏移插值行数
// 始终以 settledPage 为锚点currentPage - settledPage 确定方向(-1/0/+1
// abs(offsetFraction) 为过渡进度。
// 这样在 currentPage 跳变前后,方向和进度都是连续的:
// 跳变前: sp=8月, cp=8月, diff=0, offsetFraction>0 → 目标9月, fraction 0→0.5
// 跳变后: sp=8月, cp=9月, diff=+1 → 目标9月, fraction 0.5→0
// 以 currentPage 为基准页offsetFraction 表示基准页与可视区域左边缘的偏移:
// offsetFraction > 0基准页偏右可视区域露出下一页page+1
// offsetFraction < 0基准页偏左可视区域露出上一页page-1
// 过渡进度 = abs(offsetFraction),目标页 = page ± 1。
// 当 currentPage 跳变(如从 Jul 跳到 Aug基准页行数也随之跳变
// 但 abs(offsetFraction) 同时从 ~0.5 降到 ~0.5(连续),所以插值结果连续:
// 跳变前: cp=Jul(5行), off=+0.49 → base=5, target=Aug(6), lerp(5,6,0.49)=5.49
// 跳变后: cp=Aug(6行), off=-0.47 → base=6, target=Jul(5), lerp(6,5,0.47)=5.47 ← 连续!
val offsetFraction by remember { derivedStateOf { pagerState.currentPageOffsetFraction } }
val interpolatedWeeks = if (abs(offsetFraction) > 0.01f) {
val sp = pagerState.settledPage
val diff = pagerState.currentPage - sp // -1, 0, or +1
val targetPage = if (diff != 0) sp + diff else sp + if (offsetFraction > 0) 1 else -1
val baseWeeks = calculateWeeksCountForPage(sp, today)
val cp = pagerState.currentPage
val baseWeeks = calculateWeeksCountForPage(cp, today)
val targetPage = cp + if (offsetFraction > 0) 1 else -1
val targetWeeks = calculateWeeksCountForPage(targetPage, today)
lerp(baseWeeks.toFloat(), targetWeeks.toFloat(), abs(offsetFraction))
} else {
currentWeeksCount.toFloat()
calculateWeeksCountForPage(pagerState.currentPage, today).toFloat()
}
// 预估行高DayCell aspectRatio=1宽度 = (screenWidth - horizontalPadding) / 7
@ -111,12 +112,6 @@ fun CalendarMonthView(
val calendarAreaHeightPx = headerHeightPx + gridHeightPx + rowPaddingPx
val cardHeightPx = if (screenHeightPx > 0 && calendarAreaHeightPx > 0) screenHeightPx - calendarAreaHeightPx else 0
println("[$TAG] p=$p rowH=$rowHeightPx estRowH=$estimatedRowHeightPx effRowH=$effectiveRowHeightPx " +
"headerH=$headerHeightPx gridH=$gridHeightPx calAreaH=$calendarAreaHeightPx " +
"screenH=$screenHeightPx cardH=$cardHeightPx " +
"currentWeeks=$currentWeeksCount interpolatedWeeks=$interpolatedWeeks effectiveWeeks=$effectiveWeeks " +
"offsetFraction=$offsetFraction currentPage=${pagerState.currentPage} settledPage=${pagerState.settledPage}")
// 当 rowHeightPx 已知时,用计算的高度约束 pager否则让 pager 自由扩展以测量行高
val pagerModifier = if (rowHeightPx > 0 && gridHeightPx > 0) {
Modifier