UI Toolbox logoUI Toolbox

Animations

Window animations are USS transitions. They are in Assets/UIToolbox/Runtime/Resources/UIToolbox/Styles/WindowAnimations.uss.

The AnimationsDemo example scene shows each kind of animation on this page, with its code: presets, custom presets, AnimateBinding, your own transitions and restyled window animations.

There are two kinds of classes:

Constant (WindowAnimations) Class Kind Default
WINDOW_OPEN tb-window--anim-open Transition opacity, scale, normal duration, entrance easing.
WINDOW_CLOSE tb-window--anim-close Transition opacity, scale, fast duration, exit easing.
WINDOW_MINIMIZE tb-window--anim-minimize Transition height, fast duration, exit easing.
WINDOW_MAXIMIZE tb-window--anim-maximize Transition left, top, width, height, fast duration.
WINDOW_RESTORE tb-window--anim-restore Transition Same as maximize.
WINDOW_CLOSED tb-window--closed State opacity: 0; scale: 0.9 0.9. Open starts here and close ends here.
WINDOW_MINIMIZED tb-window--minimized State Hides tb-window-content. C# sets the chrome height.

Maximize, restore and minimize animate bounds that C# writes (left, top, width, height).

If Workspace.AnimationsEnabled is false (animations-enabled="false"), no transition plays. The changes apply at once.

Restyle a built-in animation

Put the override in a user sheet or a theme. Two ways:

  1. Change the --tb-animation-* variables. This changes all animations.
  2. Override the classes. This changes one animation.

Example: windows slide up when they open.

.tb-window--closed {
    opacity: 0;
    scale: 1 1;
    translate: 0 24px;
}

.tb-window--anim-open,
.tb-window--anim-close {
    transition-property: opacity, translate;
}

To turn off one animation, set its duration to zero:

.tb-window--anim-minimize {
    transition-duration: 0s;
}

Add an animation

Use WindowAnimations.PlayAsync or WindowAnimations.EnterAsync on your own elements.

Method Use
PlayAsync(element, transitionClass, change, cancellationToken) The element is already in a panel and its style is resolved.
EnterAsync(element, transitionClass, change, cancellationToken) The element was just added to a panel. It waits one style pass before change runs, because UI Toolkit does not transition the first resolved style.

Both methods:

  1. Add transitionClass to the element.
  2. Run change (for example, add or remove a state class).
  3. Complete when the transitions that the change started end or are cancelled. A fallback timer (longest duration plus delay plus 100 ms) completes the task if no end event arrives.
  4. Remove transitionClass.

The task completes at once, and the change still applies, if the element is not in a panel, the token is already cancelled, or transitionClass is empty.

USS:

.my-panel--hidden {
    opacity: 0;
    translate: 0 -12px;
}

.my-panel--anim-show {
    transition-property: opacity, translate;
    transition-duration: var(--tb-animation-duration-normal);
    transition-timing-function: var(--tb-animation-easing-entrance);
}

C#:

using System.Threading.Tasks;
using UIToolbox.Animation;
using UnityEngine.UIElements;

public static class PanelAnimations
{
    public static Task ShowAsync(VisualElement panel)
    {
        panel.AddToClassList("my-panel--hidden");
        return WindowAnimations.EnterAsync(panel, "my-panel--anim-show",
            () => panel.RemoveFromClassList("my-panel--hidden"));
    }
}

Animation presets

A preset is a named animation for any element. Play it from C# or from UXML, with no transition code.

await WindowAnimations.PlayPresetAsync(goldLabel, "flash");
_ = WindowAnimations.PlayPresetAsync(healthBar, "pulse", loop: true);   // until StopPreset
await WindowAnimations.PlayPresetAsync(panel, "fade-out", keep: true);  // stays hidden until StopPreset
WindowAnimations.StopPreset(healthBar, "pulse");
<ui:Label>
  <Bindings>
    <ui:DataBinding property="text" data-source-path="Gold" binding-mode="ToTarget" />
    <tb:AnimateBinding property="anim-gold" preset="flash" path="Gold" />
  </Bindings>
</ui:Label>

AnimateBinding watches the value at path, from the data source of the element. property is only a unique id, because the binding writes nothing. trigger sets when the preset plays:

trigger Plays
Change (default) Each time the value changes. The first value does not play.
Attach Once, at the first update.
Loop In a loop from the first update.
WhileTrue In a loop while the bool value is true.

Built-in presets (WindowAnimations.PRESETS):

Preset Look
fade-in From opacity 0.
fade-out To opacity 0. Use with keep.
slide-up, slide-down, slide-left, slide-right Fades in and moves in from 24 px below, 24 px above, 40 px right or 40 px left.
pop From opacity 0 and scale 0.6, with an overshoot.
shake Four fast side moves.
pulse Scale 1.08 and back.
flash Text in --tb-color-warning and scale 1.15, and back.
blink Opacity 0.25 and back.
bounce Up 12 px, down, up 5 px, down.

A preset is USS only. To add one, write these classes in any sheet. PlayPresetAsync adds and removes them:

Class Holds
.tb-anim-{name} The transition-* properties, and --tb-anim-steps: n (default 0). It is on the element while the preset plays.
.tb-anim-{name}--from Optional. The start look, with transition-duration: 0s so that it applies at once.
.tb-anim-{name}--1 to --n Optional. The steps. Each step transitions from the last.

At the end the element transitions back to its own look. Example: a glow that grows in two steps.

.tb-anim-glow {
    --tb-anim-steps: 2;
    transition-property: border-color, scale;
    transition-duration: 120ms;
}

.tb-anim-glow--1 { border-color: #ffd54a; scale: 1.1 1.1; }
.tb-anim-glow--2 { border-color: #fff3b0; scale: 1.2 1.2; }

Rules:

Per-step timing

A step rule can set its own transition-duration, transition-delay and transition-timing-function. They apply to the transition into that step. The step rule must come after the base rule, because at equal specificity the later rule wins. The return to the element's own look uses the timing of the base rule.

.tb-anim-coin { --tb-anim-steps: 2; transition-property: translate; transition-duration: 220ms; }
.tb-anim-coin--1 { translate: 0 -8px; transition-duration: 70ms; }
.tb-anim-coin--2 { translate: 0 0; transition-duration: 260ms; transition-timing-function: ease-out-bounce; }

Animation Editor

Tools > UI Toolbox > Animation Editor edits presets with a live preview, in edit mode.

The editor writes a preset between two markers. It replaces only that block on the next save:

/* tb-anim-editor: begin coin */
...
/* tb-anim-editor: end coin */

The editor opens a preset outside the markers read-only, for example the built-in presets and presets written by hand. Duplicate makes an editable copy. The editor does not save to WindowAnimations.uss.

The scope is the selector text before the preset classes, for example .kingdom .tb-toolbar Label. It makes the step rules more specific than the element rules. The base class .tb-anim-{name} is not scoped.

The preset attribute of AnimateBinding has a picker in the Inspector and in UI Builder. It lists the known presets and warns about a name that no sheet defines. Put [PresetName] on your own string field or property to get the same picker.