Skip to content

Add an animation

Use PushAnimate to give a widget hover, press, or reveal animations. The fluent API defines Init, Enter, and Leave states and per-event transitions. The returned context exposes the animated values for the current frame.

This page is the per-widget recipe. For the model behind the API, refer to Animation.

To add an animation to a widget

  1. Open the context.
    var ctx = ui.PushAnimate("button_anim")
    
  2. Define the states. .Init() sets defaults. .Enter() and .Leave() define forward and reverse transitions; they are driven automatically by the widget's hover state, so a hover animation needs no extra event hook. Each state chains per-property operators and ends with .Done():
        .Init().Color().Set(restColor).Done()
        .Enter().Color().Blend(restColor, hoverColor, 0.2f).Done()
        .Leave().Color().Blend(hoverColor, restColor, 0.2f).Done()
    
  3. Optionally, drive a state from your own condition with BoolWhileTrue. Pass a string key and a condition, then chain the per-property operators that apply while the condition is true, ending with .Done():
        .BoolWhileTrue("selected", isSelected)
             .Color().Set(selectedColor)
             .Done()
    
  4. Apply the definition.
        .Apply();
    
  5. Read animated values for the current frame from the context. .Apply() already pushes the animated scale and rotation, so content drawn before PopAnimate is transformed automatically:
    ui.FillRounded(ctx.GetColor(), 5);
    
  6. Close the scope.
    ui.PopAnimate();
    

The canonical button-hover example

using (ui.Auto().Center(120, 36))
{
    var ctx = ui.PushAnimate("save_button")
        .Init()
            .Color().Set(restColor)
            .Scale().Set(1.0f)
            .Done()
        .Enter()
            .Color().Blend(restColor, hoverColor, 0.2f)
            .Scale().Blend(1.0f, 1.05f, 0.2f)
            .Done()
        .Leave()
            .Color().Blend(hoverColor, restColor, 0.2f)
            .Scale().Blend(1.05f, 1.0f, 0.2f)
            .Done()
        .Apply();

    // Apply() pushes the animated scale automatically, so the
    // content below is already drawn scaled.
    ui.FillRounded(ctx.GetColor(), 5);
    ui.Text("Save", "roboto_mono", 14, Color.white, TextAlign.MiddleCenter);

    if (ui.Button("save"))
        SaveDocument();

    ui.PopAnimate();
}

The example animates colour and scale together. Reading both values inside one draw block keeps the visual consistent across frames.

Read values

The context returned by .Apply() exposes typed getters:

Method Returns
GetColor() The animated colour.
GetFloat() The animated float.
GetRect() The animated rect.
GetInt() The animated int.
GetScale() The animated scale factor.
GetRotate() The animated rotation, in degrees.

Each call returns the blended value for the current frame, sampled against the active state and the configured curve.

Pitfalls

Reading values before Apply. : The getters return defaults until the chain finalises. Call .Apply() first, then read.

Forgetting PopAnimate. : The animation stack accumulates open contexts. The next push nests instead of replacing. Pair every PushAnimate with a PopAnimate.

The hover state never enters. : The BoolWhileTrue condition (ui.IsHovered(), for example) is read in the wrong rect. Move the call inside the rect that holds the visual.

Animations stutter on slow frames. : The Tick UI animation timing tracks elapsed time. Long frames look like skipped frames. This behaviour is correct. Do not smooth in the animation layer.

Additional resources