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

View File

@ -1,9 +1,6 @@
package plus.rua.project.ui package plus.rua.project.ui
import androidx.compose.animation.AnimatedContent 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.FastOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
@ -74,19 +71,14 @@ private data class MiniMonthColors(
* @param selectedMonth 当前选中月份1-12 * @param selectedMonth 当前选中月份1-12
* @param today 今天的日期 * @param today 今天的日期
* @param onMonthClick 月份点击回调 * @param onMonthClick 月份点击回调
* @param sharedTransitionScope 共享元素转场作用域
* @param animatedVisibilityScope 动画可见性作用域
* @param modifier 外部布局修饰符 * @param modifier 外部布局修饰符
*/ */
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable @Composable
fun YearGridView( fun YearGridView(
year: Int, year: Int,
selectedMonth: Int, selectedMonth: Int,
today: LocalDate, today: LocalDate,
onMonthClick: (Int) -> Unit, onMonthClick: (Int) -> Unit,
sharedTransitionScope: SharedTransitionScope,
animatedVisibilityScope: AnimatedVisibilityScope,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val enterT = System.nanoTime() val enterT = System.nanoTime()
@ -176,32 +168,19 @@ fun YearGridView(
) { ) {
(0 until 3).forEach { col -> (0 until 3).forEach { col ->
val month = row * 3 + col + 1 val month = row * 3 + col + 1
with(sharedTransitionScope) { MiniMonth(
// P0: 缓存 sharedElement tween避免每次重组创建新实例 year = year,
val miniMonthTween = remember { tween<androidx.compose.ui.geometry.Rect>(400, easing = FastOutSlowInEasing) } month = month,
val seKey = "month_grid_${year}_$month" isSelected = month == selectedMonth,
MiniMonth( today = today,
year = year, days = monthDays[month - 1],
month = month, colors = colors,
isSelected = month == selectedMonth, dayLayouts = dayLayouts,
today = today, titleLayouts = titleLayouts,
days = monthDays[month - 1], weekdayLayouts = weekdayLayouts,
colors = colors, onClick = { onMonthClick(month) },
dayLayouts = dayLayouts, modifier = Modifier.weight(1f)
titleLayouts = titleLayouts, )
weekdayLayouts = weekdayLayouts,
onClick = { onMonthClick(month) },
modifier = Modifier
.weight(1f)
.sharedElement(
sharedContentState = rememberSharedContentState(
key = seKey
),
animatedVisibilityScope = animatedVisibilityScope,
boundsTransform = { _, _ -> miniMonthTween }
)
)
}
} }
} }
} }