Skip to main content

Animations

Hosanna UI offers three complementary animation layers. Start simple, then move up as you need more power.

Aggregate View Transitions

  • Screen-level transitions for NavController/TabController (e.g., Fade, Simple).
  • Configured via app.config.json keys; optionally override per call.
  • Best for navigating between screens; keeps focus and lifecycle correct.

See: Aggregate View Transitions

ViewJsonAnimator (low-level)

  • Fast renderer-field tweening using JSON maps (many nodes at once).
  • Ideal for simple property animations where you control precise fields.
  • Pair with ViewAnimator for a single renderer.

See: ViewJsonAnimator

HosannaViewAnimator (high-level)

  • State-driven animations across a view subtree using logical state keys.
  • Automatically maps logical fields to renderer updates and persists final state.
  • Best for orchestrated UI effects spanning multiple views.

See: HosannaViewAnimator

Ui And Interaction Animations

@view('UiAndInteractionAnimations')
export class UiAndInteractionAnimationsView extends BaseExampleScreenView<UiAndInteractionAnimationsState> {

  protected getViews(): ViewStruct<ViewState>[] {
    // --- edit below ---

    return [
      VGroup([
        // Example 1: Move and color change animation
        VGroup([
          Button({
            id: 'moveButton',
            text: 'Click to move',
            width: 400,
            height: 50,
          })
            .onClick(this.onMoveButtonClick.bind(this))
            .isInitialFocus(),

          AnimatedRectangle({
            id: 'moveRect',
            width: 100,
            height: 100,
            color: '#00FF00',
          })
        ]).itemSpacing(30),

        Spacer({ height: 30 }),

        // Example 2: Size animation
        VGroup([
          Button({
            id: 'sizeButton',
            text: 'Click to animate size',
            width: 400,
            height: 50,
          })
            .onClick(this.onSizeButtonClick.bind(this)),

          AnimatedRectangle({
            id: 'sizeRect',
            width: 100,
            height: 100,
            color: '#0000FF',
          })
        ]).itemSpacing(30)
        ,
      ])
        .translation([50, 50])
        .itemSpacing(20)
    ];

    // --- edit above ---
  }

  private onMoveButtonClick() {
    const rect = this.getSubView<AnimatedRectangleView>('moveRect');
    if (rect) {
      rect.animateTo({
        translation: [400, 0],
        color: '#FF00FF'
      }, 1500, { reverse: true });
    }
  }

  private onSizeButtonClick() {
    const rect = this.getSubView<AnimatedRectangleView>('sizeRect');
    if (rect) {
      rect.animateTo({
        width: 200,
        height: 200
      }, 1000, { reverse: true });
    }
  }
}