feat: 移除共享元素转场,月/年视图切换改用缩放+淡入淡出动画

This commit is contained in:
xfy 2026-06-01 11:11:45 +08:00
parent 1930bbcb7f
commit 4a8480be64
2 changed files with 37 additions and 76 deletions

View File

@ -4,7 +4,6 @@ import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
@ -12,11 +11,8 @@ import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
@ -88,14 +84,13 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import plus.rua.project.util.logd
/**
* 日历主界面包含月/周视图切换折叠动画和年视图共享元素转场
* 日历主界面包含月/周视图切换折叠动画和年视图转场
*
* 折叠时日历从月视图收缩为周视图1BottomCard 同步上移填充空间
* 通过左下角 FAB 菜单切换月/年视图使用 SharedTransitionLayout 实现共享元素转场
* 通过左下角 FAB 菜单切换月/年视图
*
* @param modifier 外部布局修饰符
*/
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun CalendarMonthView(
modifier: Modifier = Modifier,
@ -185,8 +180,6 @@ fun CalendarMonthView(
screenWidthPx = size.width
}
) {
SharedTransitionLayout {
val sharedScope = this
var lastLoggedTargetState by remember { mutableStateOf(false) }
SideEffect {
if (lastLoggedTargetState != isYearView) {
@ -198,10 +191,14 @@ fun CalendarMonthView(
targetState = isYearView,
label = "month_year_transition",
transitionSpec = {
val enter = fadeIn(tween(300, easing = FastOutSlowInEasing)) +
slideInVertically(tween(300, easing = FastOutSlowInEasing)) { it / 6 }
val exit = fadeOut(tween(200, easing = FastOutSlowInEasing)) +
slideOutVertically(tween(200, easing = FastOutSlowInEasing)) { -it / 6 }
val enter = scaleIn(
initialScale = 0.85f,
animationSpec = tween(350, easing = FastOutSlowInEasing)
) + fadeIn(tween(250, easing = FastOutSlowInEasing))
val exit = scaleOut(
targetScale = 0.85f,
animationSpec = tween(250, easing = FastOutSlowInEasing)
) + fadeOut(tween(200, easing = FastOutSlowInEasing))
enter togetherWith exit
},
modifier = Modifier.fillMaxSize()
@ -261,32 +258,20 @@ fun CalendarMonthView(
val onRowHeightMeasured = remember {
{ h: Int -> if (h > 0) rowHeightPx = h }
}
with(sharedScope) {
// P0: 缓存 sharedElement tween避免每次重组创建新实例导致动画重新计算
val sharedTween = remember { tween<androidx.compose.ui.geometry.Rect>(400, easing = FastOutSlowInEasing) }
CalendarPagerArea(
selectedDate = selectedDate,
today = today,
collapseProgress = animatedCollapseProgress,
showLegalHoliday = showLegalHoliday,
rowHeightPx = rowHeightPx,
screenWidthPx = screenWidthPx,
onDateClick = onDateClick,
onMonthChanged = onMonthChanged,
shiftKindAt = shiftKindAt,
onRowHeightMeasured = onRowHeightMeasured,
pagerState = pagerState,
modifier = Modifier
.sharedElement(
sharedContentState = rememberSharedContentState(
key = "month_grid_${currentYear}_${currentMonth}"
),
animatedVisibilityScope = this@AnimatedContent,
boundsTransform = { _, _ -> sharedTween }
)
.clipToBounds()
)
}
CalendarPagerArea(
selectedDate = selectedDate,
today = today,
collapseProgress = animatedCollapseProgress,
showLegalHoliday = showLegalHoliday,
rowHeightPx = rowHeightPx,
screenWidthPx = screenWidthPx,
onDateClick = onDateClick,
onMonthChanged = onMonthChanged,
shiftKindAt = shiftKindAt,
onRowHeightMeasured = onRowHeightMeasured,
pagerState = pagerState,
modifier = Modifier.clipToBounds()
)
BottomCardArea(
viewModel = viewModel,
today = today,
@ -365,8 +350,6 @@ fun CalendarMonthView(
coroutineScope.launch { pagerState.scrollToPage(targetPage) }
}
},
sharedTransitionScope = sharedScope,
animatedVisibilityScope = this@AnimatedContent,
modifier = Modifier
)
}
@ -374,7 +357,6 @@ fun CalendarMonthView(
composeTraceEndSection()
}
}
}
// FAB 浮动按钮
FloatingActionButton(

View File

@ -1,9 +1,6 @@
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
@ -74,19 +71,14 @@ 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
) {
val enterT = System.nanoTime()
@ -176,32 +168,19 @@ fun YearGridView(
) {
(0 until 3).forEach { col ->
val month = row * 3 + col + 1
with(sharedTransitionScope) {
// P0: 缓存 sharedElement tween避免每次重组创建新实例
val miniMonthTween = remember { tween<androidx.compose.ui.geometry.Rect>(400, easing = FastOutSlowInEasing) }
val seKey = "month_grid_${year}_$month"
MiniMonth(
year = year,
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 = seKey
),
animatedVisibilityScope = animatedVisibilityScope,
boundsTransform = { _, _ -> miniMonthTween }
)
)
}
MiniMonth(
year = year,
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)
)
}
}
}