Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8313383

CSS Transitions

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P4 P4
    • jfx23
    • javafx
    • None
    • behavioral
    • low
    • No compatibility risk is expected, since the behavior of CSS and styleable properties is unchanged if no transitions are specified.
    • Java API, File or wire format
    • JDK

      Summary

      Support CSS Transitions in JavaFX.

      Problem

      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.

      Solution

      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.

      Specification

      Change the StyleableProperty specification:

      --- a/modules/javafx.graphics/src/main/java/javafx/css/StyleableProperty.java
      +++ b/modules/javafx.graphics/src/main/java/javafx/css/StyleableProperty.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
      + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
        * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        *
        * This code is free software; you can redistribute it and/or modify it
      @@ -42,8 +42,21 @@
        * or {@link javafx.scene.Parent#getStylesheets()}</li>
        * <li>a style from {@link javafx.scene.Node#setStyle(java.lang.String)}</li>
        * </ol>
      - * <p>The {@link javafx.css.StyleablePropertyFactory StyleablePropertyFactory}
      - * greatly simplifies creating a StyleableProperty and its corresponding CssMetaData.</p>
      + * In addition to being styleable via CSS, a {@code StyleableProperty} implementation can opt to
      + * support implicit CSS transitions by extending one of the following classes:
      + * <ul>
      + *     <li>{@link StyleableBooleanProperty}
      + *     <li>{@link StyleableDoubleProperty}
      + *     <li>{@link StyleableFloatProperty}
      + *     <li>{@link StyleableIntegerProperty}
      + *     <li>{@link StyleableLongProperty}
      + *     <li>{@link StyleableObjectProperty}
      + * </ul>
      + *
      + * <p>{@link StyleablePropertyFactory} greatly simplifies creating a {@code StyleableProperty} and
      + * its corresponding {@link CssMetaData}. All properties created using {@code StyleablePropertyFactory}
      + * support implicit CSS transitions.
      + *
        * @param <T> the specific property
        * @since JavaFX 8.0
        * @see javafx.css.StyleablePropertyFactory

      Add DurationConverter.SequenceConverter:

      +    /**
      +     * Converts a sequence of parsed values to an array of {@link Duration} instances.
      +     *
      +     * @since 23
      +     */
      +    public static final class SequenceConverter extends StyleConverter<ParsedValue<ParsedValue<?, Size>, Duration>[], Duration[]> {
      +        /**
      +         * Gets the {@code SequenceConverter} instance.
      +         * @return the {@code SequenceConverter} instance
      +         */
      +        public static SequenceConverter getInstance() { ... }
      +        ...
      +    }

      Add the javafx.css.TransitionEvent class:

      /**
       * An event that signals the creation, beginning, completion and cancellation of implicit CSS transitions.
       * <p>
       * Note that this event is not raised for explicit transitions that are created using the {@link Transition} class.
       *
       * @since 23
       */
      public final class TransitionEvent extends Event {
          /**
           * Common supertype for all {@code TransitionEvent} types.
           */
          public static final EventType<TransitionEvent> ANY;
      
          /**
           * This event occurs when a transition has been created and added to the list of running
           * transitions of a {@link Node}.
           */
          public static final EventType<TransitionEvent> RUN);
      
          /**
           * This event occurs when a running transition enters its active period, which happens
           * at the end of the delay phase.
           */
          public static final EventType<TransitionEvent> START;
      
          /**
           * This event occurs when a running transition has reached the end of its active period.
           * If the transition is cancelled prior to reaching the end of its active period, this
           * event does not occur.
           */
          public static final EventType<TransitionEvent> END;
      
          /**
           * This event occurs when a running transition was cancelled before it has reached the
           * end of its active period.
           */
          public static final EventType<TransitionEvent> CANCEL;
      
          /**
           * Creates a new instance of the {@code TransitionEvent} class.
           *
           * @param eventType the event type
           * @param property the {@code StyleableProperty} that is targeted by the transition
           * @param elapsedTime the time that has elapsed since the transition has entered its active period
           * @throws NullPointerException if {@code eventType}, {@code property} or {@code elapsedTime} is {@code null}
           */
          public TransitionEvent(EventType<? extends Event> eventType,
                                 StyleableProperty<?> property,
                                 Duration elapsedTime);
      
          /**
           * Gets the {@code StyleableProperty} that is targeted by the transition.
           *
           * @return the {@code StyleableProperty}
           */
          public StyleableProperty<?> getProperty();
      
          /**
           * Gets the time that has elapsed since the transition has entered its active period,
           * not including the time spent in the delay phase.
           *
           * @return the elapsed time, or zero if the transition has not entered its active period
           */
          public Duration getElapsedTime();
      }

      Add the following enumeration, fields, and method to the javafx.animation.Interpolator class:

      /**
       * 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 23
       */
      public enum StepPosition {
          /**
           * The interval starts with a rise point when the input progress value is 0.
           * <p>
           * <img width="200" src="../scene/doc-files/easing-stepstart.svg" alt="START"/>
           */
          START,
      
          /**
           * The interval ends with a rise point when the input progress value is 1.
           * <p>
           * <img width="200" src="../scene/doc-files/easing-stepend.svg" alt="END"/>
           */
          END,
      
          /**
           * 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.
           * <p>
           * <img width="200" src="../scene/doc-files/easing-stepboth.svg" alt="BOTH"/>
           */
          BOTH,
      
          /**
           * All rise points are within the open interval (0..1).
           * <p>
           * <img width="200" src="../scene/doc-files/easing-stepnone.svg" alt="NONE"/>
           */
          NONE
      }
      
      /**
       * Built-in interpolator instance that is equivalent to {@code STEPS(1, StepPosition.START)}.
       *
       * @since 23
       */
      public static final Interpolator STEP_START = STEPS(1, StepPosition.START);
      
      /**
       * Built-in interpolator instance that is equivalent to {@code STEPS(1, StepPosition.END)}.
       *
       * @since 23
       */
      public static final 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 intervalCount the number of intervals in the step interpolator
       * @param position the {@code StepPosition} of the step interpolator
       * @throws IllegalArgumentException if {@code intervalCount} is less than 1, or if {@code intervalCount} is
       *                                  less than 2 when {@code position} is {@link StepPosition#NONE}
       * @throws NullPointerException if {@code position} is {@code null}
       * @return a new step interpolator
       *
       * @since 23
       */
      public static Interpolator STEPS(int intervalCount, StepPosition position);

      Extend the JavaFX CSS parser to support the following new properties:

      - transition-property          [ none | all | <custom‑ident># ]
      - transition-duration          <duration>#
      - transition-delay             <duration>#
      - transition-timing-function   <easing-function>#
      - transition                   <single-transition>#
      
      <single-transition> =
          [ none | all | <custom‑ident> ] || <duration> || <easing‑function> || <duration>
      
      <easing-function> =
          linear | <cubic-bezier-easing-function> | <step-easing-function> | <fx-easing-function>
      
      <cubic-bezier-easing-function> =
          ease | ease-in | ease-out | ease-in-out | cubic-bezier(<number [0,1]>, <number>, <number [0,1]>, <number>)
      
      <step-easing-function> =
          step-start | step-end | steps(<integer>[,<step-position>]?)
      
      <step-position> =
          jump-start | jump-end | jump-none | jump-both | start | end
      
      <fx-easing-function> =
          -fx-ease-in | -fx-ease-out | -fx-ease-both
      
      where:
          -fx-ease-in    equivalent to `Interpolator.EASE_IN`
          -fx-ease-out   equivalent to `Interpolator.EASE_OUT`
          -fx-ease-both  equivalent to `Interpolator.EASE_BOTH`

      Transition properties conform to the CSS Transitions specification. Easing functions conform to the CSS Easing Functions specification.

            mstrauss Michael Strauß
            mstrauss Michael Strauß
            Kevin Rushforth
            Votes:
            1 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: