feat(shift): smooth height interpolation for mini calendar swipe

滑动翻月时高度平滑过渡,不再跳变:
- 复用主日历的 interpolatedWeeks 行数插值逻辑
  (currentPageOffsetFraction 在当前页与目标页行数间 lerp)
- 行高由 BoxWithConstraints 宽度推算(格宽=宽/7,aspectRatio(1f) 使行高=格宽)
- HorizontalPager 套 Modifier.height(px).clipToBounds(),跟随手指实时变高
- 迷你月历简化版:无折叠动画,只保留展开态 gridHeightPx = rowH * weeks
This commit is contained in:
xfy 2026-07-09 17:14:54 +08:00
parent 7b1290dd66
commit 2a3089bf47

View File

@ -6,10 +6,12 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
@ -38,6 +40,8 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
@ -55,6 +59,7 @@ import kotlinx.datetime.number
import kotlinx.datetime.plus import kotlinx.datetime.plus
import kotlinx.datetime.toLocalDateTime import kotlinx.datetime.toLocalDateTime
import kotlinx.datetime.todayIn import kotlinx.datetime.todayIn
import kotlin.math.abs
import kotlin.time.Clock import kotlin.time.Clock
import kotlin.time.Instant import kotlin.time.Instant
import plus.rua.project.RephaseFlip import plus.rua.project.RephaseFlip
@ -93,6 +98,7 @@ fun ShiftCalendarGrid(
val viewMonth by remember { val viewMonth by remember {
derivedStateOf { pageToYearMonth(pagerState.currentPage, initialYear, initialMonth).second } derivedStateOf { pageToYearMonth(pagerState.currentPage, initialYear, initialMonth).second }
} }
val density = LocalDensity.current
Card( Card(
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth(),
@ -137,11 +143,35 @@ fun ShiftCalendarGrid(
} }
} }
// 滑动翻页区域:每页一个月的网格 // 用 BoxWithConstraints 拿宽度,推算行高(格宽=宽/7,aspectRatio(1f) 使行高=格宽)
BoxWithConstraints(Modifier.fillMaxWidth()) {
val cellWidthPx = constraints.maxWidth / 7
val rowHeightPx = cellWidthPx
// 行数插值:滑动时在当前页与目标页行数间线性过渡,避免高度跳变
val interpolatedWeeks by remember {
derivedStateOf {
val fraction = pagerState.currentPageOffsetFraction
if (abs(fraction) > OFFSET_FRACTION_THRESHOLD) {
val cp = pagerState.currentPage
val baseWeeks = calculateWeeksCountForPage(cp, today)
val targetPage = cp + if (fraction > 0) 1 else -1
val targetWeeks = calculateWeeksCountForPage(targetPage, today)
lerp(baseWeeks.toFloat(), targetWeeks.toFloat(), abs(fraction))
} else {
calculateWeeksCountForPage(pagerState.currentPage, today).toFloat()
}
}
}
val gridHeightPx = (rowHeightPx * interpolatedWeeks).toInt()
HorizontalPager( HorizontalPager(
state = pagerState, state = pagerState,
beyondViewportPageCount = 0, beyondViewportPageCount = 0,
modifier = Modifier.fillMaxWidth() modifier = Modifier
.fillMaxWidth()
.height(with(density) { gridHeightPx.toDp() })
.clipToBounds()
) { page -> ) { page ->
val (year, month) = pageToYearMonth(page, initialYear, initialMonth) val (year, month) = pageToYearMonth(page, initialYear, initialMonth)
MonthGrid( MonthGrid(
@ -154,6 +184,7 @@ fun ShiftCalendarGrid(
} }
} }
} }
}
if (showMonthPicker) { if (showMonthPicker) {
val initialDate = LocalDate(viewYear, Month(viewMonth), 1) val initialDate = LocalDate(viewYear, Month(viewMonth), 1)