refactor: 优化动画曲线 & 颜色变换动画性能

This commit is contained in:
Super12138 2026-02-08 23:10:55 +08:00
parent c33ed5d048
commit eb7fa59302
3 changed files with 54 additions and 26 deletions

View file

@ -38,7 +38,7 @@ android {
applicationId = "cn.super12138.todo" applicationId = "cn.super12138.todo"
minSdk = 24 minSdk = 24
targetSdk = 36 targetSdk = 36
versionCode = 985 versionCode = 986
versionName = "2.3.3" versionName = "2.3.3"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

View file

@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -85,6 +86,7 @@ fun SharedTransitionScope.TodoEditPage(
onNavigateUp = onNavigateUp onNavigateUp = onNavigateUp
) )
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
fun SharedTransitionScope.TodoEditorPage( fun SharedTransitionScope.TodoEditorPage(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -210,8 +212,12 @@ fun SharedTransitionScope.TodoEditorPage(
AnimatedVisibility( AnimatedVisibility(
visible = isCustomCategory, visible = isCustomCategory,
enter = fadeIn() + expandVertically(), enter = fadeIn(MaterialTheme.motionScheme.defaultEffectsSpec()) + expandVertically(
exit = fadeOut() + shrinkVertically() MaterialTheme.motionScheme.defaultEffectsSpec()
),
exit = fadeOut(MaterialTheme.motionScheme.defaultEffectsSpec()) + shrinkVertically(
MaterialTheme.motionScheme.defaultEffectsSpec()
)
) { ) {
TodoCategoryTextField( TodoCategoryTextField(
value = uiState.categoryContent, value = uiState.categoryContent,

View file

@ -2,16 +2,16 @@ package cn.super12138.todo.ui.pages.tasks.components
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ContentTransform
import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandIn import androidx.compose.animation.expandIn
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn import androidx.compose.animation.scaleIn
import androidx.compose.animation.shrinkOut import androidx.compose.animation.shrinkOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -22,9 +22,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -34,7 +35,7 @@ import cn.super12138.todo.R
import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.utils.VibrationUtils import cn.super12138.todo.utils.VibrationUtils
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
fun TodoTopAppBar( fun TodoTopAppBar(
selectedTodoIds: List<Int>, selectedTodoIds: List<Int>,
@ -44,8 +45,32 @@ fun TodoTopAppBar(
onDeleteSelectedTodo: () -> Unit, onDeleteSelectedTodo: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val navIconEnterTransition = fadeIn(
animationSpec = MaterialTheme.motionScheme.defaultEffectsSpec()
) + expandIn(
animationSpec = MaterialTheme.motionScheme.defaultSpatialSpec(),
expandFrom = Alignment.CenterStart
)
val navIconExitTransition = fadeOut(
animationSpec = MaterialTheme.motionScheme.defaultEffectsSpec()
) + shrinkOut(
animationSpec = MaterialTheme.motionScheme.defaultSpatialSpec(),
shrinkTowards = Alignment.CenterStart
)
val enterTransition = fadeIn(MaterialTheme.motionScheme.defaultEffectsSpec()) +
scaleIn(
animationSpec = MaterialTheme.motionScheme.defaultSpatialSpec(),
initialScale = 0.92f
)
val exitTransition = fadeOut(MaterialTheme.motionScheme.defaultEffectsSpec())
val titleTransition = ContentTransform(
targetContentEnter = enterTransition,
initialContentExit = exitTransition
)
val view = LocalView.current val view = LocalView.current
val animatedTopAppBarColors by animateColorAsState( val animatedContainerColor by animateColorAsState(
animationSpec = MaterialTheme.motionScheme.defaultEffectsSpec(),
targetValue = if (selectedMode) { targetValue = if (selectedMode) {
MaterialTheme.colorScheme.surfaceContainerHighest MaterialTheme.colorScheme.surfaceContainerHighest
} else { } else {
@ -57,8 +82,8 @@ fun TodoTopAppBar(
navigationIcon = { navigationIcon = {
AnimatedVisibility( AnimatedVisibility(
visible = selectedMode, visible = selectedMode,
enter = fadeIn() + expandIn(expandFrom = Alignment.CenterStart), enter = navIconEnterTransition,
exit = shrinkOut(shrinkTowards = Alignment.CenterStart) + fadeOut() exit = navIconExitTransition
) { ) {
IconButton( IconButton(
onClick = { onClick = {
@ -76,14 +101,7 @@ fun TodoTopAppBar(
title = { title = {
AnimatedContent( AnimatedContent(
targetState = !selectedMode, targetState = !selectedMode,
transitionSpec = { transitionSpec = { titleTransition }
(
fadeIn(animationSpec = tween(100)) +
scaleIn(initialScale = 0.92f)
).togetherWith(
fadeOut(animationSpec = tween(100))
)
}
) { ) {
if (it) { if (it) {
Text( Text(
@ -104,7 +122,11 @@ fun TodoTopAppBar(
} }
}, },
actions = { actions = {
AnimatedVisibility(visible = selectedMode) { AnimatedVisibility(
visible = selectedMode,
enter = enterTransition,
exit = exitTransition
) {
Row { Row {
IconButton( IconButton(
onClick = { onClick = {
@ -132,21 +154,21 @@ fun TodoTopAppBar(
} }
}, },
colors = TopAppBarDefaults.topAppBarColors().copy( colors = TopAppBarDefaults.topAppBarColors().copy(containerColor = Color.Transparent),
containerColor = animatedTopAppBarColors modifier = modifier.drawBehind {
), drawRect(animatedContainerColor)
modifier = modifier }
) )
} }
@Preview(locale = "zh-rCN", showBackground = true) @Preview(locale = "zh-rCN", showBackground = true)
@Composable @Composable
private fun TodoTopAppBarPreview() { private fun TodoTopAppBarPreview() {
var selectedMode by remember { mutableStateOf(false) } val selectedMode = remember { mutableStateOf(false) }
TodoTopAppBar( TodoTopAppBar(
selectedTodoIds = (1..10).toList(), selectedTodoIds = (1..10).toList(),
selectedMode = selectedMode, selectedMode = selectedMode.value,
onCancelSelect = { selectedMode = !selectedMode }, onCancelSelect = { selectedMode.value = !selectedMode.value },
onSelectAll = { }, onSelectAll = { },
onDeleteSelectedTodo = { } onDeleteSelectedTodo = { }
) )