Types of Transitions
Litho supports animated transitions between two consecutive states of the UI (LayoutStates
), which, on this page, are called:
- Before - the state of the UI before the updates, prior to the start of the transition.
- After - the state of the UI after the update, after the transition has ended.
When defining transitions, it's necessary to indicate to the framework what Component
/AnimatedProperty
pairs are to be animated. Based on the presence of the Component in before and after states, three types of transitions can be defined: Change, Appear, and Disappear.
Change transitionsβ
A transition where the target Component is present in both before and after LayoutState
s is called a Change transition.
The SimpleAllLayoutTransitionComponent
featured in the Animation Basics page features a Change transition, since the 'square' Component is always present in the layout but changes its position (X
property). The 'square' is illustrated in the following animation.
How the Change type functions internallyβ
Imagine a scenario in which layout A (the before state) is mounted (rendered on the screen) and there exists a blueprint of layout B (the after state), which the framework is ready to render. Both of these layouts have ComponentC
present, and there is a transition defined for a AnimatedProperty.P
of this component.
The framework needs to take the following steps in order to run change animations:
- Pick the current value of the
AnimatedProperty.P
from the mounted UI element (aView
or 'Drawable') that representsComponentC
on the screen. This is the start value for the animation. - Pick the animation end value of
AnimatedProperty.P
from the blueprint of layout B. - When the layout B is mounted to the screen, instead of applying the new value of 'P' immediately, keep the previous value in place, subscribe to the Choreographer, then start changing the value of
P
for the new mounted UI element on every frame until it reaches the end value.
ComponentC
is present in both trees.
Before:
Column(alignItems = YogaAlign.FLEX_START) { // AnimatedProperty.X aligned to start
child(
SolidColor( // <- ComponentC
color = YELLOW,
style = Style.width(80.dp).height(80.dp).transitionKey(context, SQUARE_KEY)))
}
After:
Column(alignItems = YogaAlign.FLEX_END) { // AnimatedProperty.X aligned to end
child(
SolidColor( // <- ComponentC
color = YELLOW,
style = Style.width(80.dp).height(80.dp).transitionKey(context, SQUARE_KEY)))
}
Appear transitionsβ
A transition where the target Component appears on screen is called an Appear transition. The target Component is not present in the before LayoutState
and is present in the after LayoutState
.
In order to run transitions, you need the start and end values of the AnimatedProperty
. The framework provides the end value and the user provides the start value.
The start value can be specified by using the .appearFrom()
method when building a Transition
, as shown in the following LayoutSpec:
@LayoutSpec
public class AppearTransitionComponentSpec {
private static final String SQUARE_KEY = "square";
@OnCreateLayout
static Component onCreateLayout(ComponentContext c, @State boolean shown) {
Component child;
if (shown) {
child =
SolidColor.create(c)
.color(YELLOW)
.widthDip(80)
.heightDip(80)
.transitionKey(SQUARE_KEY)
.build();
} else {
child = null;
}
return Column.create(c)
.heightPercent(100)
.child(child)
.clickHandler(AppearTransitionComponent.onClickEvent(c))
.alignItems(YogaAlign.FLEX_END)
.build();
}
@OnCreateTransition
static Transition onCreateTransition(ComponentContext c) {
return Transition.create(SQUARE_KEY)
.animate(AnimatedProperties.X)
.appearFrom(0f)
.animate(AnimatedProperties.ALPHA)
.appearFrom(0f);
}
@OnEvent(ClickEvent.class)
static void onClickEvent(ComponentContext c, @FromEvent View view) {
AppearTransitionComponent.onUpdateState(c);
}
@OnUpdateState
static void onUpdateState(StateValue<Boolean> shown) {
shown.set(!shown.get());
}
}
The following animation shows the Appear transition in action.
How the Appear type functions internallyβ
Using the same scenario described for the Change type:
- Pick the
appearFrom
value ofAnimatedProperty.P
from theTransition
object. This is the start value for the animation. - Pick the animation end value of
AnimatedProperty.P
from the blueprint of layout B. - When layout B is mounted to the screen, instead of applying the end value from that layout to the mounted item immediately, set the user-provided appearFrom value and drive that to the end value from the layout.
Before:
val child = null
Column(alignItems = YogaAlign.FLEX_END) {
child(child) // ComponentC is null here
}
After:
val child =
SolidColor(
color = YELLOW,
style = Style.width(80.dp).height(80.dp).transitionKey(context, SQUARE_KEY))
Column(alignItems = YogaAlign.FLEX_END) {
child(child) // ComponentC is present and will be animated.
}
Disappear transitionsβ
The Disappear transition type is the opposite of the Appear transition type.
A transition where the target Component disappears from the screen is called a Disappear transition. The target Component is present in the before LayoutState
and is not present in the after LayoutState
.
In order to configure a Disappear transition, you need to provide an end value for the property that is disappearing. The end value can be specified by using the .disappearTo()
method when building a Transition
, as shown in the following code:
useTransition(
Transition.create(SQUARE_KEY)
.animate(AnimatedProperties.X)
.appearFrom(0f)
.animate(AnimatedProperties.ALPHA)
.appearFrom(0f)
.disappearTo(0f)
.animate(AnimatedProperties.SCALE)
.disappearTo(0.5f))
}
The following animation shows the Disappear transition in action.
How the Disappear type functions internallyβ
- Pick the start value from the mounted UI element that represents the
Component
. - Retrieve the end value from the user-provided
disappearTo
value of the transition definition. - Render the after layout, but instead of removing the UI element from the screen immediately, drive the value of
AnimatedProperty
to the end value then remove the UI element.
The disappearing component no longer exists in the Component Tree, but must still exist in the view hierarchy in order to be rendered for the duration of the animation. To achieve this, the component that's being animated out is moved from its current container in the before state to the root component of the after state, and rendered above all other content. Once the animation ends, the disappearing component (and its associated View / Drawable) is fully removed.
Before:
val child =
SolidColor(
color = YELLOW,
style = Style.width(80.dp).height(80.dp).transitionKey(context, SQUARE_KEY))
Column(alignItems = YogaAlign.FLEX_END) {
child(child) // ComponentC is present and will be animated.
}
After:
val child = null
Column(alignItems = YogaAlign.FLEX_END) {
child(child) // ComponentC is null here
}
During a transition, a disappearing Component
is not part of the layout tree. Instead, it is drawn on top of all the remaining components. This may cause visual glitches in the component's z-order.