feat: 优化年月视图共享元素转场动画
- 月→年切换时自动展开折叠状态 - 将 sharedElement 精确绑定到日历网格区域 - YearGridView 为每个 MiniMonth 添加共享元素转场 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f5cc4ef2e5
commit
9ad619c105
@ -129,7 +129,15 @@ class CalendarViewModel(
|
|||||||
animJob.join()
|
animJob.join()
|
||||||
composeTraceEndSection()
|
composeTraceEndSection()
|
||||||
} else {
|
} else {
|
||||||
// 月 → 年:先启动动画(月视图开始缩小),等一帧后翻转 isYearView(年视图开始组合)
|
// 月 → 年:如果折叠,先展开
|
||||||
|
if (isCollapsed) {
|
||||||
|
_collapseAnimatable.animateTo(
|
||||||
|
targetValue = 0f,
|
||||||
|
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)
|
||||||
|
)
|
||||||
|
isCollapsed = false
|
||||||
|
}
|
||||||
|
// 先启动动画(月视图开始缩小),等一帧后翻转 isYearView(年视图开始组合)
|
||||||
composeTraceBeginSection("MonthView→YearView")
|
composeTraceBeginSection("MonthView→YearView")
|
||||||
yearViewYear = selectedDate.year
|
yearViewYear = selectedDate.year
|
||||||
_yearViewAnimatable.snapTo(0f)
|
_yearViewAnimatable.snapTo(0f)
|
||||||
|
|||||||
@ -159,25 +159,16 @@ fun CalendarMonthView(
|
|||||||
targetState = viewModel.isYearView,
|
targetState = viewModel.isYearView,
|
||||||
label = "month_year_transition",
|
label = "month_year_transition",
|
||||||
transitionSpec = {
|
transitionSpec = {
|
||||||
fadeIn(tween(300, easing = FastOutSlowInEasing)) togetherWith
|
fadeIn(tween(0)) togetherWith fadeOut(tween(0))
|
||||||
fadeOut(tween(300, easing = FastOutSlowInEasing))
|
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) { isYearView ->
|
) { isYearView ->
|
||||||
with(sharedScope) {
|
|
||||||
if (!isYearView) {
|
if (!isYearView) {
|
||||||
composeTraceBeginSection("MonthView:Compose")
|
composeTraceBeginSection("MonthView:Compose")
|
||||||
val layoutReady = rowHeightPx > 0
|
val layoutReady = rowHeightPx > 0
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.sharedBounds(
|
|
||||||
sharedContentState = rememberSharedContentState(key = "month_content"),
|
|
||||||
animatedVisibilityScope = this@AnimatedContent,
|
|
||||||
boundsTransform = { _, _ ->
|
|
||||||
tween(400, easing = FastOutSlowInEasing)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.alpha(if (layoutReady) 1f else 0f)
|
.alpha(if (layoutReady) 1f else 0f)
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
@ -197,17 +188,29 @@ fun CalendarMonthView(
|
|||||||
WeekdayHeader(
|
WeekdayHeader(
|
||||||
modifier = Modifier.fillMaxWidth().padding(bottom = ROW_PADDING_DP.dp)
|
modifier = Modifier.fillMaxWidth().padding(bottom = ROW_PADDING_DP.dp)
|
||||||
)
|
)
|
||||||
CalendarPagerArea(
|
with(sharedScope) {
|
||||||
viewModel = viewModel,
|
CalendarPagerArea(
|
||||||
today = today,
|
viewModel = viewModel,
|
||||||
rowHeightPx = rowHeightPx,
|
today = today,
|
||||||
screenWidthPx = screenWidthPx,
|
rowHeightPx = rowHeightPx,
|
||||||
onRowHeightMeasured = { h ->
|
screenWidthPx = screenWidthPx,
|
||||||
if (h > 0) rowHeightPx = h
|
onRowHeightMeasured = { h ->
|
||||||
},
|
if (h > 0) rowHeightPx = h
|
||||||
pagerState = pagerState,
|
},
|
||||||
modifier = Modifier.clipToBounds()
|
pagerState = pagerState,
|
||||||
)
|
modifier = Modifier
|
||||||
|
.sharedElement(
|
||||||
|
sharedContentState = rememberSharedContentState(
|
||||||
|
key = "month_grid_${currentYear}_${currentMonth}"
|
||||||
|
),
|
||||||
|
animatedVisibilityScope = this@AnimatedContent,
|
||||||
|
boundsTransform = { _, _ ->
|
||||||
|
tween(400, easing = FastOutSlowInEasing)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.clipToBounds()
|
||||||
|
)
|
||||||
|
}
|
||||||
BottomCardArea(
|
BottomCardArea(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
today = today,
|
today = today,
|
||||||
@ -222,13 +225,6 @@ fun CalendarMonthView(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.sharedBounds(
|
|
||||||
sharedContentState = rememberSharedContentState(key = "month_content"),
|
|
||||||
animatedVisibilityScope = this@AnimatedContent,
|
|
||||||
boundsTransform = { _, _ ->
|
|
||||||
tween(400, easing = FastOutSlowInEasing)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.padding(horizontal = HORIZONTAL_PADDING_DP.dp)
|
.padding(horizontal = HORIZONTAL_PADDING_DP.dp)
|
||||||
) {
|
) {
|
||||||
YearHeader(
|
YearHeader(
|
||||||
@ -273,13 +269,14 @@ fun CalendarMonthView(
|
|||||||
coroutineScope.launch { pagerState.scrollToPage(targetPage) }
|
coroutineScope.launch { pagerState.scrollToPage(targetPage) }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
sharedTransitionScope = sharedScope,
|
||||||
|
animatedVisibilityScope = this@AnimatedContent,
|
||||||
modifier = Modifier.alpha(crossFadeAlpha)
|
modifier = Modifier.alpha(crossFadeAlpha)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
composeTraceEndSection()
|
composeTraceEndSection()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FAB 浮动按钮
|
// FAB 浮动按钮
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
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.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.slideInVertically
|
import androidx.compose.animation.slideInVertically
|
||||||
@ -63,14 +67,19 @@ 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
|
||||||
) {
|
) {
|
||||||
composeTraceBeginSection("YearGridView:$year")
|
composeTraceBeginSection("YearGridView:$year")
|
||||||
@ -156,18 +165,30 @@ fun YearGridView(
|
|||||||
) {
|
) {
|
||||||
(0 until 3).forEach { col ->
|
(0 until 3).forEach { col ->
|
||||||
val month = row * 3 + col + 1
|
val month = row * 3 + col + 1
|
||||||
MiniMonth(
|
with(sharedTransitionScope) {
|
||||||
month = month,
|
MiniMonth(
|
||||||
isSelected = month == selectedMonth,
|
month = month,
|
||||||
today = today,
|
isSelected = month == selectedMonth,
|
||||||
days = monthDays[month - 1],
|
today = today,
|
||||||
colors = colors,
|
days = monthDays[month - 1],
|
||||||
dayLayouts = dayLayouts,
|
colors = colors,
|
||||||
titleLayouts = titleLayouts,
|
dayLayouts = dayLayouts,
|
||||||
weekdayLayouts = weekdayLayouts,
|
titleLayouts = titleLayouts,
|
||||||
onClick = { onMonthClick(month) },
|
weekdayLayouts = weekdayLayouts,
|
||||||
modifier = Modifier.weight(1f)
|
onClick = { onMonthClick(month) },
|
||||||
)
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.sharedElement(
|
||||||
|
sharedContentState = rememberSharedContentState(
|
||||||
|
key = "month_grid_${year}_$month"
|
||||||
|
),
|
||||||
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
|
boundsTransform = { _, _ ->
|
||||||
|
tween(400, easing = FastOutSlowInEasing)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user