Skip to main content

Customizing Transitions

Litho provides several APIs to customise many aspects of transitions and animate different components together.

Sequence, Stagger, and Parallel sets​

Transitions can be composed using the following:

  • Sequence - all transitions are run in order. Each transition must end before the following one starts.
  • Stagger - all transitions are a run in order. There is a fixed delay between starting each transition, and they may overlap.
  • Parallel - all transitions start at the same time but may end at different times.

The following code uses a stagger set to animate three 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());
}
}

The following animation shows this component in action.

Transition sets can be nested within each other, for example, you could have a 'stagger' of 'parallel' animation sets.

Sequences and Staggers support interrupting behaviour, which ensure a component will never jump and will always end up in the correct end position.

The sets can also be used to animate different properties of the same component, as shown 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());
}
}

The following animation shows this component in action.

Animators​

Animators affect the rate in which new values are pushed to the animated components. An Animator can be added to any transition with the .animator() builder method.

Litho provides two Animators:

These Animators can be configured to cover most use cases.

It's possible to configure a custom transition by implementing Transition.TransitionAnimator.

By default, all transitions in Litho run by the 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.

In addition, the TimingTransitionAnimator enables you to set a total time and an Interpolator. To do this, use the builder Transition.timing() that receives the total time and an optional Interpolator.

info

The default interpolator for the TimingTransitionAnimator is an AccelerateDecelerateInterpolator.

@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())));
}

The following animation shows this component in action.

TransitionEnd callback​

A Listener can be added to receive a callback when an individual transition has ended. This is done through Litho event dispatcher methods (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 instances of AnimatedProperty are added to the same transition, and all of them run at the same time, a callback will be executed for each. This callback may be useful to loop animations by updating the tree.

@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)));
}
note

Many examples can be found in the Litho Sample app: API Demos->Animation Callbacks.

A transitionEndHandler can also be added to Transition.allLayout() where the same logic applies:

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