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:
parent
7b1290dd66
commit
2a3089bf47
@ -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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user