diff --git a/core/src/main/kotlin/plus/rua/project/CalendarViewModel.kt b/core/src/main/kotlin/plus/rua/project/CalendarViewModel.kt index 63d32fc..f0fdd1e 100644 --- a/core/src/main/kotlin/plus/rua/project/CalendarViewModel.kt +++ b/core/src/main/kotlin/plus/rua/project/CalendarViewModel.kt @@ -129,7 +129,15 @@ class CalendarViewModel( animJob.join() composeTraceEndSection() } else { - // 月 → 年:先启动动画(月视图开始缩小),等一帧后翻转 isYearView(年视图开始组合) + // 月 → 年:如果折叠,先展开 + if (isCollapsed) { + _collapseAnimatable.animateTo( + targetValue = 0f, + animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f) + ) + isCollapsed = false + } + // 先启动动画(月视图开始缩小),等一帧后翻转 isYearView(年视图开始组合) composeTraceBeginSection("MonthView→YearView") yearViewYear = selectedDate.year _yearViewAnimatable.snapTo(0f) diff --git a/core/src/main/kotlin/plus/rua/project/ui/CalendarMonthView.kt b/core/src/main/kotlin/plus/rua/project/ui/CalendarMonthView.kt index d74f431..1005895 100644 --- a/core/src/main/kotlin/plus/rua/project/ui/CalendarMonthView.kt +++ b/core/src/main/kotlin/plus/rua/project/ui/CalendarMonthView.kt @@ -159,25 +159,16 @@ fun CalendarMonthView( targetState = viewModel.isYearView, label = "month_year_transition", transitionSpec = { - fadeIn(tween(300, easing = FastOutSlowInEasing)) togetherWith - fadeOut(tween(300, easing = FastOutSlowInEasing)) + fadeIn(tween(0)) togetherWith fadeOut(tween(0)) }, modifier = Modifier.fillMaxSize() ) { isYearView -> - with(sharedScope) { if (!isYearView) { composeTraceBeginSection("MonthView:Compose") val layoutReady = rowHeightPx > 0 Box( modifier = Modifier .fillMaxSize() - .sharedBounds( - sharedContentState = rememberSharedContentState(key = "month_content"), - animatedVisibilityScope = this@AnimatedContent, - boundsTransform = { _, _ -> - tween(400, easing = FastOutSlowInEasing) - } - ) .alpha(if (layoutReady) 1f else 0f) ) { Column( @@ -197,17 +188,29 @@ fun CalendarMonthView( WeekdayHeader( modifier = Modifier.fillMaxWidth().padding(bottom = ROW_PADDING_DP.dp) ) - CalendarPagerArea( - viewModel = viewModel, - today = today, - rowHeightPx = rowHeightPx, - screenWidthPx = screenWidthPx, - onRowHeightMeasured = { h -> - if (h > 0) rowHeightPx = h - }, - pagerState = pagerState, - modifier = Modifier.clipToBounds() - ) + with(sharedScope) { + CalendarPagerArea( + viewModel = viewModel, + today = today, + rowHeightPx = rowHeightPx, + screenWidthPx = screenWidthPx, + onRowHeightMeasured = { h -> + if (h > 0) rowHeightPx = h + }, + pagerState = pagerState, + modifier = Modifier + .sharedElement( + sharedContentState = rememberSharedContentState( + key = "month_grid_${currentYear}_${currentMonth}" + ), + animatedVisibilityScope = this@AnimatedContent, + boundsTransform = { _, _ -> + tween(400, easing = FastOutSlowInEasing) + } + ) + .clipToBounds() + ) + } BottomCardArea( viewModel = viewModel, today = today, @@ -222,13 +225,6 @@ fun CalendarMonthView( Column( modifier = Modifier .fillMaxSize() - .sharedBounds( - sharedContentState = rememberSharedContentState(key = "month_content"), - animatedVisibilityScope = this@AnimatedContent, - boundsTransform = { _, _ -> - tween(400, easing = FastOutSlowInEasing) - } - ) .padding(horizontal = HORIZONTAL_PADDING_DP.dp) ) { YearHeader( @@ -273,13 +269,14 @@ fun CalendarMonthView( coroutineScope.launch { pagerState.scrollToPage(targetPage) } } }, + sharedTransitionScope = sharedScope, + animatedVisibilityScope = this@AnimatedContent, modifier = Modifier.alpha(crossFadeAlpha) ) } } composeTraceEndSection() } - } } // FAB 浮动按钮 diff --git a/core/src/main/kotlin/plus/rua/project/ui/YearGridView.kt b/core/src/main/kotlin/plus/rua/project/ui/YearGridView.kt index 1ccc5db..f55d7d7 100644 --- a/core/src/main/kotlin/plus/rua/project/ui/YearGridView.kt +++ b/core/src/main/kotlin/plus/rua/project/ui/YearGridView.kt @@ -1,6 +1,10 @@ package plus.rua.project.ui import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedVisibilityScope +import androidx.compose.animation.ExperimentalSharedTransitionApi +import androidx.compose.animation.SharedTransitionScope +import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween import androidx.compose.animation.slideInVertically @@ -63,14 +67,19 @@ private data class MiniMonthColors( * @param selectedMonth 当前选中月份(1-12) * @param today 今天的日期 * @param onMonthClick 月份点击回调 + * @param sharedTransitionScope 共享元素转场作用域 + * @param animatedVisibilityScope 动画可见性作用域 * @param modifier 外部布局修饰符 */ +@OptIn(ExperimentalSharedTransitionApi::class) @Composable fun YearGridView( year: Int, selectedMonth: Int, today: LocalDate, onMonthClick: (Int) -> Unit, + sharedTransitionScope: SharedTransitionScope, + animatedVisibilityScope: AnimatedVisibilityScope, modifier: Modifier = Modifier ) { composeTraceBeginSection("YearGridView:$year") @@ -156,18 +165,30 @@ fun YearGridView( ) { (0 until 3).forEach { col -> val month = row * 3 + col + 1 - MiniMonth( - month = month, - isSelected = month == selectedMonth, - today = today, - days = monthDays[month - 1], - colors = colors, - dayLayouts = dayLayouts, - titleLayouts = titleLayouts, - weekdayLayouts = weekdayLayouts, - onClick = { onMonthClick(month) }, - modifier = Modifier.weight(1f) - ) + with(sharedTransitionScope) { + MiniMonth( + month = month, + isSelected = month == selectedMonth, + today = today, + days = monthDays[month - 1], + colors = colors, + dayLayouts = dayLayouts, + titleLayouts = titleLayouts, + weekdayLayouts = weekdayLayouts, + onClick = { onMonthClick(month) }, + modifier = Modifier + .weight(1f) + .sharedElement( + sharedContentState = rememberSharedContentState( + key = "month_grid_${year}_$month" + ), + animatedVisibilityScope = animatedVisibilityScope, + boundsTransform = { _, _ -> + tween(400, easing = FastOutSlowInEasing) + } + ) + ) + } } } }