Class Transition

java.lang.Object
limn.animation.Transition

public final class Transition extends Object
One animated float property: the toolkit's reusable "CSS transition". A widget owns a Transition per property it wants to animate (opacity, a fill level, a lerp factor, a position, …), configures it once, then just moves the target with to(float); value(), read each paint, eases toward it.

Like a CSS transition, the developer controls whether it animates (enabled), the duration, the easing curve and whether it repeats, for any property, since the value is just a number the widget maps onto whatever it draws.

Reduced motion: while the user has asked the system for less motion (SystemPreferences.reduceMotion()), to jumps as a disabled transition does, so every animation built on this class honours it without a line.

Optimized: it drives itself off the scene's frame ticker only while moving and unregisters the instant it settles, so an idle widget costs nothing. Safe: to / snap(float) are UI-thread-confined, and when the owner has no scene (headless, or detached) it simply jumps to the target: the same "final state, no animation" a test or an off-screen widget wants. A repeating one, which has no final state, starts once its owner is painted in a scene.


 private final Transition fade = new Transition(this).duration(0.15).easing(Easing.EASE_OUT);
 // on state change:
 fade.to(visible ? 1 : 0);
 // in onPaint:
 color.timesAlpha(fade.value());
 
  • Constructor Details

    • Transition

      public Transition(Widget<?> owner)
      A transition starting (and resting) at 0.
    • Transition

      public Transition(Widget<?> owner, float initial)
      A transition starting at initial, repainting owner as it runs.
  • Method Details

    • duration

      public Transition duration(double seconds)
      Sets the animation length in seconds (0 = no animation; to snaps).
    • damages

      public Transition damages(@Nullable Runnable region)
      Narrows what each frame of this animation repaints, for an animation that moves a small part of a large widget.

      By default a running transition calls owner.invalidate() once per frame, which is the whole widget — right for a button, whose animation is the button, and badly wrong for a list, whose focus ring is one row of many. Measured: a list arriving at focus repainted itself whole for eleven consecutive frames so that one row's outline could fade in. Under a full-frame default that costs nothing, because the frame was full anyway; under a partial one it is the difference between a row and a list.

      The first choice is still to give the animation an owner that is what moves — a tab strip rather than the tabbed pane. This is for when no such widget exists because the moving part is drawn by the parent itself.

      The callback must invalidate something, and enough: it is the only repaint the frame gets, so a region short of what the animation draws leaves stale pixels behind, which no assertion can see. It runs on the UI thread, once per animated frame, and must not change layout.

      Parameters:
      region - invalidates the part this animation draws, or null for the owner
      Returns:
      this
    • easing

      public Transition easing(Easing curve)
      Sets the timing curve (default Easing.EASE_OUT).
    • enabled

      public Transition enabled(boolean value)
      When false, to jumps instead of animating (default true).
    • repeat

      public Transition repeat(boolean value)
      When true, the animation ping-pongs between its endpoints forever (a pulse/breathe) until snap(float) or repeat(false) stops it.
    • sceneTime

      public Transition sceneTime(boolean value)
      Whether this transition runs on the scene's scene time (and so obeys Scene.setPaused(boolean) and Scene.setTimeScale(double)) instead of wall time. Default false.

      The default is what every widget in the toolkit wants: a hover, focus or press fade is shell feedback, and feedback that stops responding while an app pauses its own animation reads as a hang, not as a pause. Turn it on for a transition that animates content (a game object's position, a value the simulation owns), which is exactly what pausing is supposed to stop.

      Read when the transition registers itself with the scene, so flipping it mid-animation takes effect on the next run rather than the current one.

    • duration

      public double duration()
      Duration of one run, in seconds.
    • isEnabled

      public boolean isEnabled()
      Whether animating at all; when off, the value jumps to its target.
    • repeats

      public boolean repeats()
      Whether the transition restarts on completion instead of settling.
    • to

      public void to(float target)
      Animates the value toward target. Jumps immediately (no animation) when disabled, zero-duration, the owner is detached from a scene, or the user asked the system for less motion (Motion.reduced()). UI thread.

      A repeating transition asked for while its owner is in no scene does not jump, because a pulse has no final state to jump to: it waits where it is and starts the first time its owner is painted in a scene.

    • snap

      public void snap(float value)
      Jumps to value immediately, cancelling any animation. UI thread.
    • value

      public float value()
      Returns:
      the current (eased) value; read this each paint
    • target

      public float target()
      Returns:
      the value it is heading toward
    • isAnimating

      public boolean isAnimating()
      Whether the value is still moving: false once it has settled on its target.