1 /* 2 * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.animation; 27 28 import com.sun.scenario.animation.AbstractMasterTimer; 29 import javafx.beans.property.ObjectProperty; 30 import javafx.beans.property.SimpleObjectProperty; 31 import javafx.scene.Node; 32 33 /** 34 * An abstract class that contains the basic functionalities required by all 35 * {@code Transition} based animations, such as {@link PathTransition} and 36 * {@link RotateTransition}. 37 * <p> 38 * This class offers a simple framework to define animation. It provides all the 39 * basic functionality defined in {@link Animation}. {@code Transition} requires 40 * the implementation of a method {@link #interpolate(double)} which is the 41 * called in each frame, while the {@code Transition} is running. 42 * <p> 43 * In addition an extending class needs to set the duration of a single cycle 44 * with {@link Animation#setCycleDuration(javafx.util.Duration)}. This duration 45 * is usually set by the user via a duration property (as in 46 * {@link FadeTransition#durationProperty() duration}) for example. But it can also be calculated 47 * by the extending class as is done in {@link ParallelTransition} and 48 * {@link FadeTransition}. 49 * <p> 50 * Below is a simple example. It creates a small animation that updates the 51 * {@code text} property of a {@link javafx.scene.text.Text} node. It starts 52 * with an empty {@code String} and adds gradually letter by letter until the 53 * full {@code String} was set when the animation finishes. 54 * 55 * <pre> 56 * {@code 57 * 58 * final String content = "Lorem ipsum"; 59 * final Text text = new Text(10, 20, ""); 60 * 61 * final Animation animation = new Transition() { 62 * { 63 * setCycleDuration(Duration.millis(2000)); 64 * } 65 * 66 * protected void interpolate(double frac) { 67 * final int length = content.length(); 68 * final int n = Math.round(length * (float) frac); 69 * text.setText(content.substring(0, n)); 70 * } 71 * 72 * }; 73 * 74 * animation.play(); 75 * }</pre> 76 * 77 * @see Animation 78 * 79 * @since JavaFX 2.0 80 */ 81 public abstract class Transition extends Animation { 82 83 /** 84 * Controls the timing for acceleration and deceleration at each 85 * {@code Transition} cycle. 86 * <p> 87 * This may only be changed prior to starting the transition or after the 88 * transition has ended. If the value of {@code interpolator} is changed for 89 * a running {@code Transition}, the animation has to be stopped and started again to 90 * pick up the new value. 91 * <p> 92 * Default interpolator is set to {@link Interpolator#EASE_BOTH}. 93 * 94 * @defaultValue EASE_BOTH 95 */ 96 private ObjectProperty<Interpolator> interpolator; 97 private static final Interpolator DEFAULT_INTERPOLATOR = Interpolator.EASE_BOTH; 98 99 public final void setInterpolator(Interpolator value) { 100 if ((interpolator != null) || (!DEFAULT_INTERPOLATOR.equals(value))) { 101 interpolatorProperty().set(value); 102 } 103 } 104 105 public final Interpolator getInterpolator() { 106 return (interpolator == null) ? DEFAULT_INTERPOLATOR : interpolator.get(); 107 } 108 109 public final ObjectProperty<Interpolator> interpolatorProperty() { 110 if (interpolator == null) { 111 interpolator = new SimpleObjectProperty<Interpolator>( 112 this, "interpolator", DEFAULT_INTERPOLATOR 113 ); 114 } 115 return interpolator; 116 } 117 118 private Interpolator cachedInterpolator; 119 120 /** 121 * Returns the {@link Interpolator}, that was set when the 122 * {@code Transition} was started. 123 * 124 * Changing the {@link #interpolatorProperty() interpolator} of a running {@code Transition} should 125 * have no immediate effect. Instead the running {@code Transition} should 126 * continue to use the original {@code Interpolator} until it is stopped and 127 * started again. 128 * 129 * @return the {@code Interpolator} that was set when this 130 * {@code Transition} was started 131 */ 132 protected Interpolator getCachedInterpolator() { 133 return cachedInterpolator; 134 } 135 136 /** 137 * The constructor of {@code Transition}. 138 * 139 * This constructor allows to define a {@link #getTargetFramerate() target framerate}. 140 * 141 * @param targetFramerate 142 * The custom target frame rate for this {@code Transition} 143 */ 144 public Transition(double targetFramerate) { 145 super(targetFramerate); 146 } 147 148 /** 149 * The constructor of {@code Transition}. 150 */ 151 public Transition() { 152 } 153 154 // For testing purposes 155 Transition(AbstractMasterTimer timer) { 156 super(timer); 157 } 158 159 /** 160 * Returns the first non-{@code null} target {@code Node} in the parent hierarchy of 161 * this {@code Transition}, or {@code null} if such a node is not found. 162 * <p> 163 * A parent animation is one that can have child animations. Examples are 164 * {@link javafx.animation.SequentialTransition SequentialTransition} and 165 * {@link javafx.animation.ParallelTransition ParallelTransition}. A parent animation can 166 * also be a child of another parent animation. 167 * <p> 168 * Note that if this {@code Transition} has a target node set and is not a parent animation, 169 * it will be ignored during the call as this method only queries parent animations. 170 * @return the target {@code Node} 171 */ 172 protected Node getParentTargetNode() { 173 return (parent != null && parent instanceof Transition) ? 174 ((Transition)parent).getParentTargetNode() : null; 175 } 176 177 /** 178 * The method {@code interpolate()} has to be provided by implementations of 179 * {@code Transition}. While a {@code Transition} is running, this method is 180 * called in every frame. 181 * 182 * The parameter defines the current position with the animation. At the 183 * start, the fraction will be {@code 0.0} and at the end it will be 184 * {@code 1.0}. How the parameter increases, depends on the 185 * {@link #interpolatorProperty() interpolator}, e.g. if the 186 * {@code interpolator} is {@link Interpolator#LINEAR}, the fraction will 187 * increase linear. 188 * 189 * This method must not be called by the user directly. 190 * 191 * @param frac 192 * The relative position 193 */ 194 protected abstract void interpolate(double frac); 195 196 private double calculateFraction(long currentTicks, long cycleTicks) { 197 final double frac = cycleTicks <= 0 ? 1.0 : (double) currentTicks / cycleTicks; 198 return cachedInterpolator.interpolate(0.0, 1.0, frac); 199 } 200 201 @Override 202 boolean startable(boolean forceSync) { 203 return super.startable(forceSync) 204 && ((getInterpolator() != null) || (!forceSync && (cachedInterpolator != null))); 205 } 206 207 @Override 208 void sync(boolean forceSync) { 209 super.sync(forceSync); 210 if (forceSync || (cachedInterpolator == null)) { 211 cachedInterpolator = getInterpolator(); 212 } 213 } 214 215 @Override 216 void doPlayTo(long currentTicks, long cycleTicks) { 217 setCurrentTicks(currentTicks); 218 interpolate(calculateFraction(currentTicks, cycleTicks)); 219 } 220 221 @Override 222 void doJumpTo(long currentTicks, long cycleTicks, boolean forceJump) { 223 setCurrentTicks(currentTicks); 224 if (getStatus() != Status.STOPPED || forceJump) { 225 sync(false); 226 interpolate(calculateFraction(currentTicks, cycleTicks)); 227 } 228 } 229 }