Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Compose Transition Animation

Compose Transition Animation

Avatar for Yuki Anzai

Yuki Anzai

March 31, 2023
Tweet

More Decks by Yuki Anzai

Other Decks in Technology

Transcript

  1. var item by remember { mutableStateOf(Item.A) } Box( contentAlignment =

    Alignment.Center, modifier = Modifier.fillMaxWidth().height(300.dp) ) { Spacer( modifier = Modifier .size(200.dp) .background( when (item) { Item.A -> Color.Red Item.B -> Color.Blue } ) ) } Column(modifier = Modifier.padding(horizontal = 16.dp)) { Item.values().forEach { RadioButtonWithText( text = it.name, selected = item == it, onClick = { item = it } ) } } ᶃJUFN͕มΘΔ ᶄSFDPNQPTF͕૸ͬͯมΘΔ
  2. "OJNBUFE$POUFOU w ঢ়ଶʢ4UBUFʣ͕มΘͬͨͱ͖ͷ੾Γସ͑ʢFOUFSFYJUʣΛΞχϝʔγϣϯ ͢Δ var myState by remember { mutableStateOf(Item.A)

    } AnimatedContent( label = "myState", targetState = myState ) { // content lambda ͷҾ਺ (it) Λ࢖͏ // when(myState) ͸μϝ when(it) { ... } }
  3. var item by remember { mutableStateOf(Item.A) } AnimatedContent( label =

    "item", targetState = item, ) { Box( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxWidth() .height(300.dp) ) { Spacer( modifier = Modifier .size(200.dp) .background( when (it) { Item.A -> Color.Red Item.B -> Color.Blue } ) ) } } … "OJNBUFE$POUFOUΛ࢖͏ DPOUFOUMBNCEBͷҾ਺Λ࢖͏
  4. var item by remember { mutableStateOf(Item.A) } AnimatedContent( label =

    "item", targetState = item, transitionSpec = { fadeIn(tween(300, 150)) + slideIntoContainer( towards = when (targetState) { Item.A -> SlideDirection.End Item.B -> SlideDirection.Start }, initialOffset = { it / 4 }, animationSpec = tween(1000, 150) ) with fadeOut(tween(150)) } ) { … } … NTͰGBEFPVU NT଴ͬͨ͋ͱʢGBEFPVU͕ऴΘͬͨޙʣ NTͰGBEFJO NT଴ͬͨ͋ͱ ʢGBEFPVU͕ऴΘͬͨޙʣ NTͰTMJEFJO
  5. "OJNBUFE$POUFOUͷத਎ΛΈΔͱʜ @ExperimentalAnimationApi @Composable fun <S> AnimatedContent( targetState: S, … )

    { val transition = updateTransition(targetState = targetState, label = label) transition.AnimatedContent( modifier, transitionSpec, contentAlignment, content = content ) } VQEBUF5SBOTJUJPO Λ࢖͍ͬͯΔ 5SBOTJUJPOͷ"OJNBUFE$POUFOUΛ࢖͍ͬͯΔ
  6. VQEBUF5SBOTJUJPO w ঢ়ଶʢ஋ʣͷมߋʹෳ਺ͷΞχϝʔγϣϯΛඥ͚͍ͮͨͱ͖ʹ࢖͏ var item by remember { mutableStateOf(Item.A) }

    val itemTransition = updateTransition(targetState = item, label = "item") ঢ়ଶ FOVNDMBTT*UFN\" #^ 5SBOTJUJPOΠϯελϯε͕ฦΔ UBSHFU4UBUFʹ4UBUFͷ஋Λ౉͢
  7. VQEBUF5SBOTJUJPO w ঢ়ଶʢ஋ʣͷมߋʹෳ਺ͷΞχϝʔγϣϯΛඥ͚͍ͮͨͱ͖ʹ࢖͏ var item by remember { mutableStateOf(Item.A) }

    val itemTransition = updateTransition(targetState = item, label = "item") val size by itemTransition.animateDp(label = "size") { when (it) { Item.A -> 100.dp Item.B -> 200.dp } } val color by itemTransition.animateColor(label = "color") { when (it) { Item.A -> Color.Red Item.B -> Color.Blue } } 5SBOTJUJPOBOJNBUF ͰΞχϝʔγϣϯͷ4UBUF5Λऔಘ͢Δ
  8. var item by remember { mutableStateOf(Item.A) } val itemTransition =

    updateTransition(targetState = item, label = "item") val size by itemTransition.animateDp(label = "size") { when (it) { Item.A -> 100.dp Item.B -> 200.dp } } val color by itemTransition.animateColor(label = "color") { when (it) { Item.A -> Color.Red Item.B -> Color.Blue } } Spacer( modifier = Modifier .size(size) .background(color) ) JUFN͕มΘΔͱTJ[Fͱ CBDLHSPVOE྆ํΞχϝʔγϣϯ͢Δ
  9. var item by remember { mutableStateOf(Item.A) } val itemTransition =

    updateTransition(targetState = item, label = "item") val size by itemTransition.animateDp(label = "size") { when (it) { Item.A -> 100.dp Item.B -> 200.dp } } val color by itemTransition.animateColor(label = "color") { when (it) { Item.A -> Color.Red Item.B -> Color.Blue } } Spacer( modifier = Modifier .size(size) .background(color) )
  10. VQEBUF5SBOTJUJPO ͷத਎ΛΈΔͱʜ @Composable fun <T> updateTransition( targetState: T, label: String?

    = null ): Transition<T> { val transition = remember { Transition(targetState, label = label) } transition.animateTo(targetState) DisposableEffect(transition) { … } return transition } 5SBOTJUJPOΛੜ੒
  11. .VUBCMF5SBOTJUJPO4UBUFΛอ࣋ @Composable fun <T> updateTransition( targetState: T, label: String? =

    null ): Transition<T> { val transition = remember { Transition(targetState, label = label) } transition.animateTo(targetState) DisposableEffect(transition) { … } return transition } @Stable class Transition<S> @PublishedApi internal constructor( private val transitionState: MutableTransitionState<S>, val label: String? = null ) { internal constructor( initialState: S, label: String? ) : this(MutableTransitionState(initialState), label) … } 5SBOTJUJPOΛੜ੒
  12. @Stable class Transition<S> @PublishedApi internal constructor( private val transitionState: MutableTransitionState<S>,

    val label: String? = null ) { internal constructor( initialState: S, label: String? ) : this(MutableTransitionState(initialState), label) … } JOJUJBM4UBUFͰ.VUBCMF5SBOTJUJPO4UBUFΛੜ੒ @Composable fun <T> updateTransition( targetState: T, label: String? = null ): Transition<T> { val transition = remember { Transition(targetState, label = label) } transition.animateTo(targetState) DisposableEffect(transition) { … } return transition }
  13. @Stable class Transition<S> @PublishedApi internal constructor( private val transitionState: MutableTransitionState<S>,

    val label: String? = null ) { internal constructor( initialState: S, label: String? ) : this(MutableTransitionState(initialState), label) … } @Composable fun <T> updateTransition( targetState: T, label: String? = null ): Transition<T> { val transition = remember { Transition(targetState, label = label) } transition.animateTo(targetState) DisposableEffect(transition) { … } return transition } ͬͪ͜ͷίϯετϥΫλΛ࢖͏VQEBUF5SBOTJUJPO ΋͋Δʁ
  14. @Stable class Transition<S> @PublishedApi internal constructor( private val transitionState: MutableTransitionState<S>,

    val label: String? = null ) { internal constructor( initialState: S, label: String? ) : this(MutableTransitionState(initialState), label) … } @Composable fun <T> updateTransition( targetState: T, label: String? = null ): Transition<T> { val transition = remember { Transition(targetState, label = label) } transition.animateTo(targetState) DisposableEffect(transition) { … } return transition } ͬͪ͜ͷίϯετϥΫλΛ࢖͏VQEBUF5SBOTJUJPO ΋͋Δʁ ͋Γ·͢
  15. @Stable class Transition<S> @PublishedApi internal constructor( private val transitionState: MutableTransitionState<S>,

    val label: String? = null ) { internal constructor( initialState: S, label: String? ) : this(MutableTransitionState(initialState), label) … } @Composable fun <T> updateTransition( transitionState: MutableTransitionState<T>, label: String? = null ): Transition<T> { val transition = remember(transitionState) { Transition(transitionState = transitionState, label) } transition.animateTo(transitionState.targetState) DisposableEffect(transition) { … } return transition } .VUBCMF5SBOTJUJPO4UBUFΛͱΔVQEBUF5SBOTBDUJPO
  16. .VUBCMF5SBOTJUJPO4UBUF͕มΘͬͨΒ5SBOTJUJPO͸ ੜ੒͠௚͞ΕΔ .VUBCMF5SBOTJUJPO4UBUFͷUBSHFU4UBUFΛ BOJNBUF5P ʹ౉͢ @Composable fun <T> updateTransition( transitionState:

    MutableTransitionState<T>, label: String? = null ): Transition<T> { val transition = remember(transitionState) { Transition(transitionState = transitionState, label) } transition.animateTo(transitionState.targetState) DisposableEffect(transition) { … } return transition }
  17. .VUBCMF5SBOTJUJPO4UBUF class MutableTransitionState<S>(initialState: S) { var currentState: S by mutableStateOf(initialState)

    internal set var targetState: S by mutableStateOf(initialState) val isIdle: Boolean get() = (currentState == targetState) && !isRunning internal var isRunning: Boolean by mutableStateOf(false) } 5SBOTJUJPO͔Βߋ৽͞ΕΔ ͜ΕΛมߋ͢Δͱ5SBOTJUJPOUBSHFU4UBUF͕ ߋ৽͞ΕͯΞχϝʔγϣϯ͕։࢝͞ΕΔ ʢΞχϝʔγϣϯऴྃ࣌ͳͲʣ5SBOTJUJPO͕৽͍͠ ঢ়ଶʹͳͬͨͱ͖ʹ5SBOTJUJPO͔Βߋ৽͞ΕΔ
  18. var item by remember { mutableStateOf(Item.A) } val transitionState =

    remember { MutableTransitionState(item) } transitionState.targetState = item val itemTransition = updateTransition(transitionState = transitionState, label = "item") val itemTransition = updateTransition(targetState = item, label = "item") val size by itemTransition.animateDp(label = "size") { … } val color by itemTransition.animateColor(label = "color") { … } Spacer( modifier = Modifier .size(size) .background(color) )
  19. var item by remember { mutableStateOf(Item.A) } val transitionState =

    remember { MutableTransitionState(item) } transitionState.targetState = item val itemTransition = updateTransition(transitionState = transitionState, label = "item") val itemTransition = updateTransition(targetState = item, label = "item") val size by itemTransition.animateDp(label = "size") { … } val color by itemTransition.animateColor(label = "color") { … } Spacer( modifier = Modifier .size(size) .background(color) ) .VUBCMF5SBOTJUJPO4UBUF͸SFNFNCFS͢Δ
  20. var item by remember { mutableStateOf(Item.A) } val transitionState =

    remember { MutableTransitionState(item) } transitionState.targetState = item val itemTransition = updateTransition(transitionState = transitionState, label = "item") val itemTransition = updateTransition(targetState = item, label = "item") val size by itemTransition.animateDp(label = "size") { … } val color by itemTransition.animateColor(label = "color") { … } Spacer( modifier = Modifier .size(size) .background(color) ) .VUBCMF5SBOTJUJPO4UBUFͷUBSHFU4UBUFΛߋ৽͢Δ
  21. var item by remember { mutableStateOf(Item.A) } val transitionState =

    remember { MutableTransitionState(item) } transitionState.targetState = item val itemTransition = updateTransition(transitionState = transitionState, label = "item") val itemTransition = updateTransition(targetState = item, label = "item") val size by itemTransition.animateDp(label = "size") { … } val color by itemTransition.animateColor(label = "color") { … } Spacer( modifier = Modifier .size(size) .background(color) ) VQEBUF5SBOTJUJPO ʹ.VUBCMF5SBOTJUJPO4UBUFΛ౉͢
  22. .VUBCMF5SBOTJUJPO4UBUFͳΒͰ͖Δ͜ͱ w ࠷ॳͷUBSHFU4UBUFͱ͸ผͷॳظঢ়ଶΛͱΕΔ w DPNQPTJUJPOʹೖͬͨͱ͖ʹΞχϝʔγϣϯͤ͞Δ͜ͱ͕Ͱ͖Δ var visible by remember {

    mutableStateOf(true) } val transitionState = remember { MutableTransitionState(false) } transitionState.targetState = visible val itemTransition = updateTransition(transitionState = transitionState, label = "item") DPNQPTJUJPO࣌ʹ ॳظঢ়ଶ͸GBMTFɺ UBSHFU4UBUF͸USVFͳͷͰ Ξχϝʔγϣϯ͕૸Δ
  23. .VUBCMF5SBOTJUJPO4UBUFͳΒͰ͖Δ͜ͱ w ࠷ॳͷUBSHFU4UBUFͱ͸ผͷॳظঢ়ଶΛͱΕΔ w DPNQPTJUJPOʹೖͬͨͱ͖ʹΞχϝʔγϣϯͤ͞Δ͜ͱ͕Ͱ͖Δ w .VUBCMF5SBOTJUJPO4UBUFΛ࠶ੜ੒͢Δ͜ͱͰɺΞχϝʔγϣϯͷϦελʔτ ΛτϦΨʔͰ͖Δ val transitionState

    = remember(trigger) { MutableTransitionState(false) } val itemTransition = updateTransition(transitionState = transitionState, label = "item") USJHHFS͕มΘΔͱ.VUBCMF5SBOTJUJPO4UBUF͕࡞Γ௚͞ΕΔͷͰ 5SBOTJUJPO΋࡞Γ௚͞ΕΔ
  24. var item by remember { mutableStateOf(Item.A) } val transitionState =

    remember { MutableTransitionState(item) } transitionState.targetState = item val itemTransition = updateTransition( transitionState = transitionState, label = "item" ) itemTransition.AnimatedContent( transitionSpec = { … } ) { … } VQEBUF5SBOTJUJPO Λ࢖͏ 5SBOTJUJPOͷ"OJNBUFE$POUFOUΛ࢖͏
  25. var item by remember { mutableStateOf(Item.A) } val transitionState =

    remember { MutableTransitionState(Item.B) } transitionState.targetState = item val itemTransition = updateTransition( transitionState = transitionState, label = "item" ) … μϝͳ৔߹ DPNQPTJUJPO࣌ͷΞχϝʔγϣϯͰ #ͷͱ͖ͷ੨͍࢛͕֯Ұॠදࣔ͞Εͯ͠·͏
  26. var item by remember { mutableStateOf(Item.A) } val transitionState =

    remember { MutableTransitionState<Item?>(null) } transitionState.targetState = item val itemTransition = updateTransition( transitionState = transitionState, label = "item" ) itemTransition.AnimatedContent( transitionSpec = { … } ) { Box(…) { if (it != null) { Spacer(…) } } } ॳظঢ়ଶΛOVMMʹ͢Δ DPNQPTJUJPO࣌ʹ͸ OVMMˠ*UFN" ͷΞχϝʔγϣϯ͕૸Δ
  27. @ExperimentalAnimationApi @Composable fun <S> Transition<S>.AnimatedContent( modifier: Modifier = Modifier, transitionSpec:

    AnimatedContentScope<S>.() -> ContentTransform = { fadeIn(animationSpec = tween(220, delayMillis = 90)) + scaleIn(initialScale = 0.92f, animationSpec = tween(220, delayMillis = 90)) with fadeOut(animationSpec = tween(90)) }, contentAlignment: Alignment = Alignment.TopStart, contentKey: (targetState: S) -> Any? = { it }, content: @Composable() AnimatedVisibilityScope.(targetState: S) -> Unit ) {
  28. interface AnimatedVisibilityScope { /** * [transition] allows custom enter/exit animations

    to be specified. It will run simultaneously * with the built-in enter/exit transitions specified in [AnimatedVisibility]. */ @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET") @get:ExperimentalAnimationApi @ExperimentalAnimationApi val transition: Transition<EnterExitState> … }
  29. itemTransition.AnimatedContent( … ) { val rotationY by transition.animateFloat( label =

    "rotationY", transitionSpec = { tween(durationMillis = 1000, delayMillis = 150) } ) { state -> when (state) { EnterExitState.PreEnter -> when (it) { Item.A -> 45f Item.B, null -> -45f } EnterExitState.Visible -> 0f EnterExitState.PostExit -> 0f } } Box(…) { if (it != null) { Spacer( modifier = Modifier .size(200.dp) .graphicsLayer { this.rotationY = rotationY this.cameraDistance = 40f } …
  30. itemTransition.AnimatedContent( … ) { val rotationY by transition.animateFloat( label =

    "rotationY", transitionSpec = { tween(durationMillis = 1000, delayMillis = 150) } ) { state -> when (state) { EnterExitState.PreEnter -> when (it) { Item.A -> 45f Item.B, null -> -45f } EnterExitState.Visible -> 0f EnterExitState.PostExit -> 0f } } Box(…) { if (it != null) { Spacer( modifier = Modifier .size(200.dp) .graphicsLayer { this.rotationY = rotationY this.cameraDistance = 40f } … "OJNBUFE7JTJCJMJUZ4DPQFͷUSBOTJUJPOͰ BOJNBUF99Λ࢖͏
  31. itemTransition.AnimatedContent( … ) { val rotationY by transition.animateFloat( label =

    "rotationY", transitionSpec = { tween(durationMillis = 1000, delayMillis = 150) } ) { state -> when (state) { EnterExitState.PreEnter -> when (it) { Item.A -> 45f Item.B, null -> -45f } EnterExitState.Visible -> 0f EnterExitState.PostExit -> 0f } } Box(…) { if (it != null) { Spacer( modifier = Modifier .size(200.dp) .graphicsLayer { this.rotationY = rotationY this.cameraDistance = 40f } … FOUFS࣌ͷॳظ஋ FOUFS࣌ͷUBSHFU஋FYJU࣌ͷॳظ஋ FYJU࣌ͷUBSHFU஋
  32. itemTransition.AnimatedContent( … ) { val rotationY by transition.animateFloat( label =

    "rotationY", transitionSpec = { tween(durationMillis = 1000, delayMillis = 150) } ) { state -> when (state) { EnterExitState.PreEnter -> when (it) { Item.A -> 45f Item.B, null -> -45f } EnterExitState.Visible -> 0f EnterExitState.PostExit -> 0f } } Box(…) { if (it != null) { Spacer( modifier = Modifier .size(200.dp) .graphicsLayer { this.rotationY = rotationY this.cameraDistance = 40f } … SPUBUJPO:ΛΞχϝʔγϣϯ