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

Support interpolation for backgrounds and borders

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P4 P4
    • jfx24
    • javafx
    • None
    • source, binary, behavioral
    • low
    • Hide
      The expected compatibility risk is low:
      1. The addition of the interpolate(T, double) method to various types that didn't have it before.
      2. Changing `Color.interpolate(T, double)` to return `this` if the returned value would be equal. Note that the method already returns `this` for t<=0 and `endValue` for t>=1.
      3. A new constructor for `TransitionEvent` is added, which requires another parameter. The old constructor is deprecated for removal. Note that `TransitionEvent` is a CSS event and cannot be meaningfully created by application developers. In addition to that, `TransitionEvent` is introduced with JFX 23 and the new constructor is added in JFX 24 (the time window is very small).
      Show
      The expected compatibility risk is low: 1. The addition of the interpolate(T, double) method to various types that didn't have it before. 2. Changing `Color.interpolate(T, double)` to return `this` if the returned value would be equal. Note that the method already returns `this` for t<=0 and `endValue` for t>=1. 3. A new constructor for `TransitionEvent` is added, which requires another parameter. The old constructor is deprecated for removal. Note that `TransitionEvent` is a CSS event and cannot be meaningfully created by application developers. In addition to that, `TransitionEvent` is introduced with JFX 23 and the new constructor is added in JFX 24 (the time window is very small).
    • Java API
    • JDK

      Summary

      Support interpolation for backgrounds and borders, allowing them to be targeted by CSS transitions.

      Problem

      JDK-8311895 adds support for CSS transitions, which are implicit animations for interpolatable types (i.e. primitives or types that implement the Interpolatable interface).

      Background and Border objects are deeply immutable, but not interpolatable. Consider the following Background, which describes the background of a Region:

      Background {
          fills = [
      BackgroundFill { fill = Color.RED } ] }

      Since backgrounds are deeply immutable, changing the region's background to another color requires the construction of a new Background, containing a new BackgroundFill, containing the new Color.

      Animating the background color using a CSS transition therefore requires the entire Background object graph to be interpolatable in order to generate intermediate backgrounds.

      Solution

      Supporting interpolation for backgrounds and borders requires the following types to implement the Interpolatable interface:

      • Insets
      • Background
      • BackgroundFill
      • BackgroundImage
      • BackgroundPosition
      • BackgroundSize
      • Border
      • BorderImage
      • BorderStroke
      • BorderWidths
      • CornerRadii
      • Stop
      • Paint and all of its subclasses
      • Margins (internal type)
      • BorderImageSlices (internal type)

      As of now, only Color, Point2D, and Point3D are interpolatable. Each of these classes is an aggregate of double values, which are combined using linear interpolation. However, many of the new interpolatable classes comprise of not only double values, but a whole range of other types. This requires us to more precisely define what we mean by "interpolation":

      • Default: Component types that implement Interpolatable are interpolated by calling the interpolate(Object, double) method.
      • Linear: Two components are combined by linear interpolation such that t = 0 produces the start value, and t = 1 produces the end value. This interpolation type is usually applicable for numeric components.
      • Discrete: If two components cannot be meaningfully combined, the intermediate component value is equal to the start value for t < 0.5 and equal to the end value for t >= 0.5.
      • Pairwise: Two lists are combined by pairwise interpolation. If the start list has fewer elements than the target list, the missing elements are copied from the target list. If the start list has more elements than the target list, the excess elements are discarded.

      Some component types are interpolated in specific ways not covered here, which will be explained in their respective documentation.

      Every component of an interpolatable class specifies its interpolation type with the new @interpolationType javadoc tag. For example, this is the specification of the CornerRadii.topLeftHorizontalRadius component:

      /**
       * The length of the horizontal radii of the top-left corner.
       *
       * @return the length of the horizontal radii of the top-left corner
       * @interpolationType <a href="../../animation/Interpolatable.html#linear">linear</a> if both values are
       *                    absolute or both values are {@link #isTopLeftHorizontalRadiusAsPercentage() percentages},
       *                    <a href="../../animation/Interpolatable.html#discrete">discrete</a> otherwise
       */
      public double getTopLeftHorizontalRadius()

      In the generated documentation, this javadoc tag shows up as a new entry in the specification list.

      CSS allows developers to specify independent transitions for sub-properties. Consider the following example:

      .button {
          -fx-border-color: red;
          -fx-border-width: 5;
      
          transition: -fx-border-color 1s linear,
                      -fx-border-width 4s ease;
      }
      
      .button:hover {
          -fx-border-color: green;
          -fx-border-width: 10;
      }

      We have two independent transitions (each with a different duration and easing function) that target two sub-properties of the same Border. We can't use normal interpolation between the before-change style and the after-change style of the border, as the Interpolatable interface doesn't allow us to specify separate transitions per sub-property.

      Instead, we add a new interface (internal for now) that is implemented by BorderConverter and BackgroundConverter:

      public interface SubPropertyConverter<T> {
          /**
           * Converts a map of CSS values to the target type.
           *
           * @param values the constituent values
           * @return the converted object
           */
          T convert(Map<CssMetaData<? extends Styleable, ?>, Object> values);
      
          /**
           * Converts an object back to a map of its constituent values (deconstruction).
           * The returned map can be passed into {@link #convert(Map)} to reconstruct the object.
           *
           * @param value the object
           * @return a {@code Map} of the constituent values
           */
          Map<CssMetaData<? extends Styleable, ?>, Object> convertBack(T value);
      }

      This allows us to use BorderConverter and BackgroundConverter to decompose solid objects into their CSS-addressable component parts, animate each of the components, and then reconstruct a new solid object that incorporates the effects of several independent transitions.

      More specifically, if the CSS subsystem applies a new value via StyleableProperty.applyStyle(newValue):

      If the style converter of newValue implements SubPropertyConverter: 1. Deconstruct oldValue and newValue into their components. 2. For each component, determine if a transition was specified; if so, start a transition from oldValue.componentN to newValue.componentN with semantics as described above. 3. For each frame, collect the effects of all component transitions, and reconstruct the current value as a solid object. Otherwise, if newValue implements Interpolatable: Start a regular transition using Interpolatable.interpolate().

            mstrauss Michael Strauß
            mstrauss Michael Strauß
            Kevin Rushforth
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: