refactor: 优化 FAB 共享元素转场动画
This commit is contained in:
parent
fd2dbe7735
commit
da0728d390
4 changed files with 80 additions and 31 deletions
|
|
@ -2,6 +2,7 @@ package cn.super12138.todo.ui.navigation
|
||||||
|
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.animation.SharedTransitionLayout
|
import androidx.compose.animation.SharedTransitionLayout
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.navigation3.runtime.NavKey
|
import androidx.navigation3.runtime.NavKey
|
||||||
|
|
@ -22,28 +23,9 @@ import cn.super12138.todo.ui.pages.settings.SettingsMain
|
||||||
import cn.super12138.todo.ui.pages.tasks.TasksPage
|
import cn.super12138.todo.ui.pages.tasks.TasksPage
|
||||||
import cn.super12138.todo.ui.theme.fadeThrough
|
import cn.super12138.todo.ui.theme.fadeThrough
|
||||||
import cn.super12138.todo.ui.theme.materialSharedAxisX
|
import cn.super12138.todo.ui.theme.materialSharedAxisX
|
||||||
|
import cn.super12138.todo.ui.theme.veilFade
|
||||||
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
||||||
|
|
||||||
|
|
||||||
private const val INITIAL_OFFSET_FACTOR = 0.10f
|
|
||||||
|
|
||||||
private fun settingsTransition() = NavDisplay.transitionSpec {
|
|
||||||
materialSharedAxisX(
|
|
||||||
initialOffsetX = { (it * INITIAL_OFFSET_FACTOR).toInt() },
|
|
||||||
targetOffsetX = { -(it * INITIAL_OFFSET_FACTOR).toInt() }
|
|
||||||
)
|
|
||||||
} + NavDisplay.popTransitionSpec {
|
|
||||||
materialSharedAxisX(
|
|
||||||
initialOffsetX = { -(it * INITIAL_OFFSET_FACTOR).toInt() },
|
|
||||||
targetOffsetX = { (it * INITIAL_OFFSET_FACTOR).toInt() }
|
|
||||||
)
|
|
||||||
} + NavDisplay.predictivePopTransitionSpec {
|
|
||||||
materialSharedAxisX(
|
|
||||||
initialOffsetX = { -(it * INITIAL_OFFSET_FACTOR).toInt() },
|
|
||||||
targetOffsetX = { (it * INITIAL_OFFSET_FACTOR).toInt() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalAnimationApi::class)
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun TopNavigation(
|
fun TopNavigation(
|
||||||
|
|
@ -55,7 +37,33 @@ fun TopNavigation(
|
||||||
backStack.removeLast()
|
backStack.removeLast()
|
||||||
}
|
}
|
||||||
|
|
||||||
// val veilColor = MaterialTheme.colorScheme.surfaceDim
|
val veilColor = MaterialTheme.colorScheme.surfaceDim
|
||||||
|
fun editorTransition() = NavDisplay.transitionSpec {
|
||||||
|
veilFade(veilColor)
|
||||||
|
} + NavDisplay.popTransitionSpec {
|
||||||
|
veilFade(veilColor)
|
||||||
|
} + NavDisplay.predictivePopTransitionSpec {
|
||||||
|
veilFade(veilColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
val initialOffestFactor = 0.10f
|
||||||
|
fun settingsTransition() = NavDisplay.transitionSpec {
|
||||||
|
materialSharedAxisX(
|
||||||
|
initialOffsetX = { (it * initialOffestFactor).toInt() },
|
||||||
|
targetOffsetX = { -(it * initialOffestFactor).toInt() }
|
||||||
|
)
|
||||||
|
} + NavDisplay.popTransitionSpec {
|
||||||
|
materialSharedAxisX(
|
||||||
|
initialOffsetX = { -(it * initialOffestFactor).toInt() },
|
||||||
|
targetOffsetX = { (it * initialOffestFactor).toInt() }
|
||||||
|
)
|
||||||
|
} + NavDisplay.predictivePopTransitionSpec {
|
||||||
|
materialSharedAxisX(
|
||||||
|
initialOffsetX = { -(it * initialOffestFactor).toInt() },
|
||||||
|
targetOffsetX = { (it * initialOffestFactor).toInt() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
SharedTransitionLayout {
|
SharedTransitionLayout {
|
||||||
NavDisplay(
|
NavDisplay(
|
||||||
backStack = backStack.backStack,
|
backStack = backStack.backStack,
|
||||||
|
|
@ -88,7 +96,7 @@ fun TopNavigation(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
entry<TodoScreen.Editor.Add> {
|
entry<TodoScreen.Editor.Add>(metadata = editorTransition()) {
|
||||||
TodoAddPage(
|
TodoAddPage(
|
||||||
onSave = {
|
onSave = {
|
||||||
viewModel.addTodo(it)
|
viewModel.addTodo(it)
|
||||||
|
|
@ -98,7 +106,7 @@ fun TopNavigation(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
entry<TodoScreen.Editor.Edit> { editorArgs ->
|
entry<TodoScreen.Editor.Edit>(metadata = editorTransition()) { editorArgs ->
|
||||||
TodoEditPage(
|
TodoEditPage(
|
||||||
toDo = editorArgs.toDo,
|
toDo = editorArgs.toDo,
|
||||||
onSave = {
|
onSave = {
|
||||||
|
|
|
||||||
|
|
@ -53,10 +53,13 @@ fun SharedTransitionScope.TodoAddPage(
|
||||||
onNavigateUp: () -> Unit
|
onNavigateUp: () -> Unit
|
||||||
) = TodoEditorPage(
|
) = TodoEditorPage(
|
||||||
toDo = null,
|
toDo = null,
|
||||||
modifier = modifier.sharedBounds(
|
modifier = modifier
|
||||||
sharedContentState = rememberSharedContentState(key = Constants.KEY_TODO_FAB_TRANSITION),
|
.sharedBounds(
|
||||||
animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
sharedContentState = rememberSharedContentState(key = Constants.KEY_TODO_FAB_TRANSITION),
|
||||||
),
|
animatedVisibilityScope = LocalNavAnimatedContentScope.current,
|
||||||
|
resizeMode = SharedTransitionScope.ResizeMode.RemeasureToBounds
|
||||||
|
)
|
||||||
|
.skipToLookaheadSize(), // 这个修饰符必须放后面
|
||||||
onSave = onSave,
|
onSave = onSave,
|
||||||
onDelete = {},
|
onDelete = {},
|
||||||
onNavigateUp = onNavigateUp
|
onNavigateUp = onNavigateUp
|
||||||
|
|
@ -76,13 +79,14 @@ fun SharedTransitionScope.TodoEditPage(
|
||||||
animatedVisibilityScope = LocalNavAnimatedContentScope.current,
|
animatedVisibilityScope = LocalNavAnimatedContentScope.current,
|
||||||
resizeMode = SharedTransitionScope.ResizeMode.RemeasureToBounds
|
resizeMode = SharedTransitionScope.ResizeMode.RemeasureToBounds
|
||||||
),
|
),
|
||||||
|
//TODO: 没想好加不加 .skipToLookaheadSize(),
|
||||||
onSave = onSave,
|
onSave = onSave,
|
||||||
onDelete = onDelete,
|
onDelete = onDelete,
|
||||||
onNavigateUp = onNavigateUp
|
onNavigateUp = onNavigateUp
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TodoEditorPage(
|
fun SharedTransitionScope.TodoEditorPage(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
toDo: TodoEntity? = null,
|
toDo: TodoEntity? = null,
|
||||||
onSave: (TodoEntity) -> Unit,
|
onSave: (TodoEntity) -> Unit,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.zIndex
|
|
||||||
import androidx.navigation3.ui.LocalNavAnimatedContentScope
|
import androidx.navigation3.ui.LocalNavAnimatedContentScope
|
||||||
import cn.super12138.todo.R
|
import cn.super12138.todo.R
|
||||||
import cn.super12138.todo.constants.Constants
|
import cn.super12138.todo.constants.Constants
|
||||||
|
|
@ -87,7 +86,8 @@ fun SharedTransitionScope.TasksPage(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.sharedBounds(
|
.sharedBounds(
|
||||||
sharedContentState = rememberSharedContentState(key = Constants.KEY_TODO_FAB_TRANSITION),
|
sharedContentState = rememberSharedContentState(key = Constants.KEY_TODO_FAB_TRANSITION),
|
||||||
animatedVisibilityScope = animatedVisibilityScope
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
|
resizeMode = SharedTransitionScope.ResizeMode.RemeasureToBounds
|
||||||
)
|
)
|
||||||
.animateFloatingActionButton(
|
.animateFloatingActionButton(
|
||||||
visible = !inSelectedMode,
|
visible = !inSelectedMode,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package cn.super12138.todo.ui.theme
|
||||||
import androidx.compose.animation.ContentTransform
|
import androidx.compose.animation.ContentTransform
|
||||||
import androidx.compose.animation.EnterTransition
|
import androidx.compose.animation.EnterTransition
|
||||||
import androidx.compose.animation.ExitTransition
|
import androidx.compose.animation.ExitTransition
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.animation.core.FastOutLinearInEasing
|
import androidx.compose.animation.core.FastOutLinearInEasing
|
||||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||||
import androidx.compose.animation.core.LinearOutSlowInEasing
|
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||||
|
|
@ -12,6 +13,9 @@ import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.scaleIn
|
import androidx.compose.animation.scaleIn
|
||||||
import androidx.compose.animation.slideInHorizontally
|
import androidx.compose.animation.slideInHorizontally
|
||||||
import androidx.compose.animation.slideOutHorizontally
|
import androidx.compose.animation.slideOutHorizontally
|
||||||
|
import androidx.compose.animation.unveilIn
|
||||||
|
import androidx.compose.animation.veilOut
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 来自:https://github.com/Ashinch/ReadYou/blob/main/app/src/main/java/me/ash/reader/ui/motion/MaterialSharedAxis.kt
|
* 来自:https://github.com/Ashinch/ReadYou/blob/main/app/src/main/java/me/ash/reader/ui/motion/MaterialSharedAxis.kt
|
||||||
|
|
@ -114,4 +118,37 @@ fun fadeThroughOut(
|
||||||
durationMillis = durationMillis,
|
durationMillis = durationMillis,
|
||||||
easing = FastOutLinearInEasing
|
easing = FastOutLinearInEasing
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun veilFade(
|
||||||
|
initialColor: Color,
|
||||||
|
durationMillis: Int = 200
|
||||||
|
): ContentTransform = ContentTransform(
|
||||||
|
veilFadeIn(
|
||||||
|
initialColor = initialColor,
|
||||||
|
durationMillis = durationMillis
|
||||||
|
),
|
||||||
|
veilFadeOut(
|
||||||
|
initialColor = initialColor,
|
||||||
|
durationMillis = durationMillis
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
|
fun veilFadeIn(
|
||||||
|
initialColor: Color,
|
||||||
|
durationMillis: Int = DefaultMotionDuration,
|
||||||
|
): EnterTransition =
|
||||||
|
fadeIn(
|
||||||
|
) + unveilIn(
|
||||||
|
initialColor = initialColor
|
||||||
|
)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
|
fun veilFadeOut(
|
||||||
|
initialColor: Color,
|
||||||
|
durationMillis: Int = DefaultMotionDuration,
|
||||||
|
): ExitTransition =
|
||||||
|
fadeOut() + veilOut(
|
||||||
|
targetColor = initialColor
|
||||||
|
)
|
||||||
Loading…
Reference in a new issue