Skip to main content

Customizing Transitions

After the Animation basics the next natural step would be to create more complex transition animations. Litho provides several APIs to customize many aspects of the transitions and how different components can be animated together.

Staggers, Sequences, and Parallel Sets#

Synchronising transitions objects can be done by wrapping them in one of the following animations sets: parallel, sequence and stagger. This transition sets can be nested within each other, e.g. you can have a stagger of parallel animation sets.

Example using a stagger set to animate 3 different rectangles.

@LayoutSpec
public class StaggerTransitionComponentSpec {
private static final String YELLOW_KEY = "YELLOW_KEY";
private static final String BLUE_KEY = "BLUE_KEY";
private static final String PURPLE_KEY = "PURPLE_KEY";
private static final int PURPLE_COLOR = Color.rgb(144, 29, 191);
@OnCreateLayout
static Component onCreateLayout(ComponentContext c, @State boolean shown) {
return Row.create(c)
.heightPercent(100)
.child(
Row.create(c)
.child(
SolidColor.create(c)
.widthDip(90)
.heightDip(40)
.transitionKey(YELLOW_KEY)
.color(Color.YELLOW))
.child(
SolidColor.create(c)
.widthDip(90)
.heightDip(40)
.transitionKey(BLUE_KEY)
.color(Color.BLUE))
.child(
SolidColor.create(c)
.widthDip(90)
.heightDip(40)
.transitionKey(PURPLE_KEY)
.color(PURPLE_COLOR)))
.clickHandler(StaggerTransitionComponent.onClickEvent(c))
.alignItems(shown ? YogaAlign.FLEX_END : YogaAlign.FLEX_START)
.build();
}
@OnCreateTransition
static Transition onCreateTransition(ComponentContext c) {
return Transition.stagger(
100,
Transition.create(YELLOW_KEY).animate(AnimatedProperties.Y),
Transition.create(BLUE_KEY).animate(AnimatedProperties.Y),
Transition.create(PURPLE_KEY).animate(AnimatedProperties.Y));
}
@OnEvent(ClickEvent.class)
static void onClickEvent(ComponentContext c, @FromEvent View view) {
StaggerTransitionComponent.onUpdateState(c);
}
@OnUpdateState
static void onUpdateState(StateValue<Boolean> shown) {
shown.set(!shown.get());
}
}

Sequences and staggers also support interrupting behavior, trying to preserve the guarantee that a component will never jump and will always end up in the correct final position.

We can also use this sets to animate different properties of the same component, like in the following example.

@LayoutSpec
public class StaggerTransitionSameComponentSpec {
private static final String YELLOW_KEY = "YELLOW_KEY";
@OnCreateLayout
static Component onCreateLayout(ComponentContext c, @State boolean shown) {
return Row.create(c)
.heightPercent(100)
.child(
Row.create(c)
.child(
SolidColor.create(c)
.widthDip(90)
.heightDip(40)
.transitionKey(YELLOW_KEY)
.color(Color.YELLOW)))
.clickHandler(StaggerTransitionSameComponent.onClickEvent(c))
.justifyContent(shown ? YogaJustify.FLEX_END : YogaJustify.FLEX_START)
.alignItems(shown ? YogaAlign.FLEX_END : YogaAlign.FLEX_START)
.build();
}
@OnCreateTransition
static Transition onCreateTransition(ComponentContext c) {
return Transition.stagger(
50,
Transition.create(YELLOW_KEY).animate(AnimatedProperties.Y),
Transition.create(YELLOW_KEY).animate(AnimatedProperties.X));
}
@OnEvent(ClickEvent.class)
static void onClickEvent(ComponentContext c, @FromEvent View view) {
StaggerTransitionSameComponent.onUpdateState(c);
}
@OnUpdateState
static void onUpdateState(StateValue<Boolean> shown) {
shown.set(!shown.get());
}
}

Animators#

Animators affect the rate in which we push new values to the animating components. An Animator can be added to any transition by using .animator() builder setting. We provide two animators (spring based SpringTransitionAnimator and a general timing-based TimingTransitionAnimator) that can be configured to cover most use cases. It is also possible to add a custom by implementing Transition.TransitionAnimator. By default, all transitions in Litho run by SpringTransitionAnimator which is physics based and naturally interruptible. You can tune the parameters of this spring by creating another Animator using Transition.springWith() and pass custom tension and friction. . On the other hand, TimingTransitionAnimator provides more customisation as we can set a total time and an Interpolator. To do this we provide an easy builder Transition.timing() that received the total time and an optional Interpolator.

info

The TimingTransitionAnimator default interpolator is an AccelerateDecelerateInterpolator so keep in mind that adding this animator to only a few transitions in a set (like the example below) could create unwanted behavior.

@OnCreateTransition
static Transition onCreateTransition(ComponentContext c) {
return Transition.parallel(
Transition.create(YELLOW_KEY)
.animate(AnimatedProperties.Y)
.animator(Transition.springWithConfig(120, 12)),
Transition.create(BLUE_KEY).animate(AnimatedProperties.Y).animator(Transition.timing(1000)),
Transition.create(PURPLE_KEY)
.animate(AnimatedProperties.Y)
.animator(Transition.timing(1000, new BounceInterpolator())));
}

Transition end callback#

A listener can be added to receive a callback when an individual transition has ended. This is done through the Litho event dispatcher. See Events overview. The TransitionEndEvent will be called with the transition key and the specific AnimatedProperty that has been animated for that key. If multiple AnimatedPropertys are added to the same transition, and all of them run at the same time, a callback will be excecuted for each one of those. In many cases this callback may be useful to loop animations by updating the tree. Many examples can be found in the Litho Sample app: API Demos->Animation Callbacks.

@OnEvent(TransitionEndEvent.class)
static void onTransitionEndEvent(
ComponentContext c,
@FromEvent String transitionKey,
@FromEvent AnimatedProperty property,
@State boolean isLooping) {
SequenceTransitionLoopComponent.onUpdateState(c, false);
}
@OnCreateTransition
static Transition onCreateTransition(ComponentContext c) {
return Transition.sequence(
Transition.create(YELLOW_KEY).animate(AnimatedProperties.Y),
Transition.create(BLUE_KEY).animate(AnimatedProperties.Y),
Transition.create(PURPLE_KEY)
.animate(AnimatedProperties.Y)
.transitionEndHandler(SequenceTransitionLoopComponent.onTransitionEndEvent(c)));
}

You can also add the transition end handler to the Transition.allLayout() and the same logic applies.

@OnCreateTransition
static Transition onCreateTransition(ComponentContext c) {
return Transition.allLayout().transitionEndHandler(MyComponentSpec.onTransitionEndEvent(c));
}