Skip to content

Instantly share code, notes, and snippets.

@mstr2
Last active July 17, 2023 19:10
Show Gist options
  • Save mstr2/c72f8c9faa87de14926978f517a6018a to your computer and use it in GitHub Desktop.
Save mstr2/c72f8c9faa87de14926978f517a6018a to your computer and use it in GitHub Desktop.

Goals

Support CSS Transitions in JavaFX.

Non-Goals

It is not a goal to

  • change the semantics of explicit transitions and animations of the javafx.animation framework
  • provide a programmatic API for implicit transitions
  • support CSS Animations

Motivation

CSS Transitions is a universally supported web standard that allows developers to specify how a CSS property changes its value smoothly from one value to another. Here's how an implicit transition is defined in a stylesheet:

.button {
    -fx-opacity: 0.8;
    transition: -fx-opacity 1s;
}

.button:hover {
    -fx-opacity: 1.0;
}

CSS Transitions makes it easy to define animated transitions and create rich and fluid user experiences. CSS Transitions also nicely complements control skins: while skins define the structure and semantics of a control, implicit transitions define its dynamic appearance.

Description

The transition pseudo-property is added to styleable nodes, consisting of the following sub-properties:

  • transition-property
  • transition-duration
  • transition-delay
  • transition-timing-function

It is special and distinct from all other CSS properties for three reasons:

  1. There is no API to access this property in Java code.
  2. It is not returned from Node.getClassCssMetaData.
  3. It is guaranteed to be applied before all other properties.

When a property value is changed by the CSS engine, we look whether a transition has been specified for the given property. If it has, the value change is animated over time. This works for all primitive property types, as well as object properties with javafx.animation.Interpolatable values.

Note that in order for this to work reliably, we must apply the transition pseudo-property before all other properties, as otherwise the CSS engine might not know that a transition was specified for a given property by the time the property value is changed.

It is important to note that an implicit transition is only started when the property value is changed by the CSS engine, but not when it is changed programmatically by calling Property.setValue(T) or via a binding. This maximizes compatibility with code that assumes that programmatic value changes happen instantly.

Applications don't need to keep track of running transitions, as they are automatically cancelled when their associated control is removed from the scene graph or becomes invisible.

TransitionEvent

The new event javafx.css.TransitionEvent signals the creation, beginning, completion and cancellation of CSS transitions. Applications can use this event to react to running transitions, for example by allowing a running transition to complete before reconfiguring the user interface or switching to a new page.

Easing Functions

The transition-timing-function sub-property is used to specify easing functions. Here is a list of all easing functions available in CSS Easing Functions:

  • linear
  • ease, ease-in, ease-out, ease-in-out, cubic-bezier(x1, y1, x2, y2)
  • step-start, step-end, steps(intervals, step-position) [where step-position can be one of the following: jump-start, jump-end, jump-none, jump-both, start, end]

Easing functions are implemented using javafx.animation.Interpolator in JavaFX. While it may seem straightforward to re-use existing interpolator implementations like Interpolator.EASE_IN, Interpolator.EASE_OUT, etc., the definitions of those interpolators don't match the definitions of CSS Easing Functions. In order to be a truthful implementation of the standard, implicit transitions use the CSS definitions, not the SMIL 3.0 definitions of the existing interpolator implementations. Additionally, the following easing functions will also be supported by JavaFX CSS:

  • -fx-ease-in, which corresponds to Interpolator.EASE_IN
  • -fx-ease-out, which corresponds to Interpolator.EASE_OUT
  • -fx-ease-both, which corresponds to Interpolator.EASE_BOTH

The following new interpolators will be added to javafx.animation.Interpolator:

public abstract class Interpolator {
    ...

    /**
     * Specifies the step position of a step interpolator.
     * <p>
     * The step position determines the location of rise points in the input progress interval, which are the
     * locations on the input progress axis where the output progress value jumps from one step to the next.
     *
     * @since 22
     */
    public enum StepPosition {
        /**
         * The interval starts with a rise point when the input progress value is 0.
         */
        START,

        /**
         * The interval ends with a rise point when the input progress value is 1.
         */
        END,

        /**
         * All rise points are within the open interval (0..1).
         */
        BOTH,

        /**
         * The interval starts with a rise point when the input progress value is 0,
         * and ends with a rise point when the input progress value is 1.
         */
        NONE
    }

    /**
     * Built-in interpolator instance that is equivalent to {@code STEPS(1, StepPosition.START)}.
     *
     * @since 22
     */
    public static Interpolator STEP_START = STEPS(1, StepPosition.START);

    /**
     * Built-in interpolator instance that is equivalent to {@code STEPS(1, StepPosition.END)}.
     *
     * @since 22
     */
    public static Interpolator STEP_END = STEPS(1, StepPosition.END);

    /**
     * Creates a step interpolator that divides the input time into a series of intervals, each
     * interval being equal in length, where each interval maps to a constant output time value.
     * The output time value is determined by the {@link StepPosition}.
     *
     * @param intervals the number of intervals in the step interpolator
     * @param position the {@code StepPosition} of the step interpolator
     * @return a new step interpolator
     *
     * @since 22
     */
    public static Interpolator STEPS(int intervals, StepPosition position) { ... }

    ...
}

Future enhancements

  • In a follow-up enhancement, Insets, Background, BackgroundFill, and CornerRadii will implement the Interpolatable interface to allow more types to be implicitly animated.

  • CSS Animations might be implemented, which is a keyframe-based animation framework for CSS.

  • An API to programmatically define CSS transitions might be added. Not doing this right away allows the implementation to move around a bit (which it might do if we choose to also do CSS Animations).

Alternatives

Applications that want to offer animated user experiences can use the existing javafx.animation framework. Animated controls can be created by subclassing existing skins and hooking into different kinds of input events and property change events to trigger animations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment