Move collapse animation from animateFloatAsState to ViewModel Animatable with spring spec

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
xfy 2026-05-14 14:32:43 +08:00
parent e53c3d8705
commit 35cbcaf430
3 changed files with 34 additions and 15 deletions

View File

@ -1,8 +1,11 @@
package plus.rua.project
import androidx.compose.animation.core.Animatable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.datetime.DatePeriod
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
@ -19,7 +22,7 @@ data class CalendarDay(
val isSelected: Boolean
)
class CalendarViewModel {
class CalendarViewModel(private val coroutineScope: CoroutineScope) {
private val today: LocalDate = Clock.System.todayIn(TimeZone.currentSystemDefault())
var selectedDate by mutableStateOf(today)
@ -28,8 +31,8 @@ class CalendarViewModel {
var isCollapsed by mutableStateOf(false)
private set
var collapseProgress by mutableStateOf(0f)
private set
private val _collapseAnimatable = Animatable(0f)
val collapseProgress: Float get() = _collapseAnimatable.value
val currentYear: Int get() = selectedDate.year
@Suppress("DEPRECATION")
@ -40,13 +43,31 @@ class CalendarViewModel {
}
fun collapse() {
isCollapsed = true
collapseProgress = 1f
if (isCollapsed) return
coroutineScope.launch {
_collapseAnimatable.animateTo(
targetValue = 1f,
animationSpec = androidx.compose.animation.core.spring(
dampingRatio = 0.8f,
stiffness = 400f
)
)
isCollapsed = true
}
}
fun expand() {
if (!isCollapsed) return
isCollapsed = false
collapseProgress = 0f
coroutineScope.launch {
_collapseAnimatable.animateTo(
targetValue = 0f,
animationSpec = androidx.compose.animation.core.spring(
dampingRatio = 0.8f,
stiffness = 400f
)
)
}
}
fun getIsoWeekNumber(date: LocalDate): Int {

View File

@ -1,6 +1,5 @@
package plus.rua.project.ui
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@ -37,22 +36,19 @@ fun CalendarMonthPage(
Column(modifier = modifier) {
weeks.forEachIndexed { weekIndex, week ->
val animatedProgress = animateFloatAsState(
targetValue = collapseProgress,
label = "collapse-$weekIndex"
).value
val progress = collapseProgress
val isAboveSelected = weekIndex < selectedWeekIndex
val isBelowSelected = weekIndex > selectedWeekIndex
val offsetY = when {
isAboveSelected -> -animatedProgress * 200f
isBelowSelected -> animatedProgress * 200f
isAboveSelected -> -progress * 200f
isBelowSelected -> progress * 200f
else -> 0f
}
val alpha = when {
isAboveSelected || isBelowSelected -> 1f - animatedProgress
isAboveSelected || isBelowSelected -> 1f - progress
else -> 1f
}

View File

@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@ -20,9 +21,10 @@ import plus.rua.project.CalendarViewModel
@Composable
fun CalendarMonthView(
viewModel: CalendarViewModel = remember { CalendarViewModel() },
modifier: Modifier = Modifier
) {
val coroutineScope = rememberCoroutineScope()
val viewModel = remember { CalendarViewModel(coroutineScope) }
val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) }
var currentYear by remember { mutableIntStateOf(viewModel.currentYear) }
var currentMonth by remember { mutableIntStateOf(viewModel.currentMonth) }