1 /* 2 * Copyright (c) 2010, 2016, 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 javafx.beans.property.DoubleProperty; 29 import javafx.beans.property.ObjectProperty; 30 import javafx.beans.property.ObjectPropertyBase; 31 import javafx.beans.property.SimpleDoubleProperty; 32 import javafx.beans.property.SimpleObjectProperty; 33 import javafx.scene.Node; 34 import javafx.util.Duration; 35 36 /** 37 * This {@code Transition} creates a fade effect animation that spans its 38 * {@code duration}. This is done by updating the {@code opacity} variable of 39 * the {@code node} at regular interval. 40 * <p> 41 * It starts from the {@code fromValue} if provided else uses the {@code node}'s 42 * {@code opacity} value. 43 * <p> 44 * It stops at the {@code toValue} value if provided else it will use start 45 * value plus {@code byValue}. 46 * <p> 47 * The {@code toValue} takes precedence if both {@code toValue} and 48 * {@code byValue} are specified. 49 * 50 * <p> 51 * Code Segment Example: 52 * </p> 53 * 54 * <pre> 55 * <code> 56 * import javafx.scene.shape.*; 57 * import javafx.animation.*; 58 * 59 * ... 60 * 61 * Rectangle rect = new Rectangle (100, 40, 100, 100); 62 * rect.setArcHeight(50); 63 * rect.setArcWidth(50); 64 * rect.setFill(Color.VIOLET); 65 * 66 * FadeTransition ft = new FadeTransition(Duration.millis(3000), rect); 67 * ft.setFromValue(1.0); 68 * ft.setToValue(0.3); 69 * ft.setCycleCount(4); 70 * ft.setAutoReverse(true); 71 * 72 * ft.play(); 73 * 74 * ... 75 * 76 * </code> 77 * </pre> 78 * 79 * @see Transition 80 * @see Animation 81 * 82 * @since JavaFX 2.0 83 */ 84 public final class FadeTransition extends Transition { 85 private static final double EPSILON = 1e-12; 86 87 private double start; 88 private double delta; 89 90 /** 91 * The target node of this {@code Transition}. 92 * <p> 93 * It is not possible to change the target {@code node} of a running 94 * {@code FadeTransition}. If the value of {@code node} is changed for a 95 * running {@code FadeTransition}, the animation has to be stopped and 96 * started again to pick up the new value. 97 */ 98 private ObjectProperty<Node> node; 99 private static final Node DEFAULT_NODE = null; 100 101 public final void setNode(Node value) { 102 if ((node != null) || (value != null /* DEFAULT_NODE */)) { 103 nodeProperty().set(value); 104 } 105 } 106 107 public final Node getNode() { 108 return (node == null)? DEFAULT_NODE : node.get(); 109 } 110 111 public final ObjectProperty<Node> nodeProperty() { 112 if (node == null) { 113 node = new SimpleObjectProperty<Node>(this, "node", DEFAULT_NODE); 114 } 115 return node; 116 } 117 118 private Node cachedNode; 119 120 /** 121 * The duration of this {@code FadeTransition}. 122 * <p> 123 * It is not possible to change the {@code duration} of a running 124 * {@code FadeTransition}. If the value of {@code duration} is changed for a 125 * running {@code FadeTransition}, the animation has to be stopped and 126 * started again to pick up the new value. 127 * <p> 128 * Note: While the unit of {@code duration} is a millisecond, the 129 * granularity depends on the underlying operating system and will in 130 * general be larger. For example animations on desktop systems usually run 131 * with a maximum of 60fps which gives a granularity of ~17 ms. 132 * 133 * Setting duration to value lower than {@link Duration#ZERO} will result 134 * in {@link IllegalArgumentException}. 135 * 136 * @defaultValue 400ms 137 */ 138 private ObjectProperty<Duration> duration; 139 private static final Duration DEFAULT_DURATION = Duration.millis(400); 140 141 public final void setDuration(Duration value) { 142 if ((duration != null) || (!DEFAULT_DURATION.equals(value))) { 143 durationProperty().set(value); 144 } 145 } 146 147 public final Duration getDuration() { 148 return (duration == null)? DEFAULT_DURATION : duration.get(); 149 } 150 151 public final ObjectProperty<Duration> durationProperty() { 152 if (duration == null) { 153 duration = new ObjectPropertyBase<Duration>(DEFAULT_DURATION) { 154 155 @Override 156 public void invalidated() { 157 try { 158 setCycleDuration(getDuration()); 159 } catch (IllegalArgumentException e) { 160 if (isBound()) { 161 unbind(); 162 } 163 set(getCycleDuration()); 164 throw e; 165 } 166 } 167 168 @Override 169 public Object getBean() { 170 return FadeTransition.this; 171 } 172 173 @Override 174 public String getName() { 175 return "duration"; 176 } 177 }; 178 } 179 return duration; 180 } 181 182 /** 183 * Specifies the start opacity value for this {@code FadeTransition}. 184 * <p> 185 * It is not possible to change {@code fromValue} of a running 186 * {@code FadeTransition}. If the value of {@code fromValue} is changed for 187 * a running {@code FadeTransition}, the animation has to be stopped and 188 * started again to pick up the new value. 189 * 190 * @defaultValue {@code Double.NaN} 191 */ 192 private DoubleProperty fromValue; 193 private static final double DEFAULT_FROM_VALUE = Double.NaN; 194 195 public final void setFromValue(double value) { 196 if ((fromValue != null) || (!Double.isNaN(value) /* DEFAULT_FROM_VALUE */ )) { 197 fromValueProperty().set(value); 198 } 199 } 200 201 public final double getFromValue() { 202 return (fromValue == null)? DEFAULT_FROM_VALUE : fromValue.get(); 203 } 204 205 public final DoubleProperty fromValueProperty() { 206 if (fromValue == null) { 207 fromValue = new SimpleDoubleProperty(this, "fromValue", DEFAULT_FROM_VALUE); 208 } 209 return fromValue; 210 } 211 212 /** 213 * Specifies the stop opacity value for this {@code FadeTransition}. 214 * <p> 215 * It is not possible to change {@code toValue} of a running 216 * {@code FadeTransition}. If the value of {@code toValue} is changed for a 217 * running {@code FadeTransition}, the animation has to be stopped and 218 * started again to pick up the new value. 219 * 220 * @defaultValue {@code Double.NaN} 221 */ 222 private DoubleProperty toValue; 223 private static final double DEFAULT_TO_VALUE = Double.NaN; 224 225 public final void setToValue(double value) { 226 if ((toValue != null) || (!Double.isNaN(value))) { 227 toValueProperty().set(value); 228 } 229 } 230 231 public final double getToValue() { 232 return (toValue == null)? DEFAULT_TO_VALUE : toValue.get(); 233 } 234 235 public final DoubleProperty toValueProperty() { 236 if (toValue == null) { 237 toValue = new SimpleDoubleProperty(this, "toValue", DEFAULT_TO_VALUE); 238 } 239 return toValue; 240 } 241 242 /** 243 * Specifies the incremented stop opacity value, from the start, of this 244 * {@code FadeTransition}. 245 * <p> 246 * It is not possible to change {@code byValue} of a running 247 * {@code FadeTransition}. If the value of {@code byValue} is changed for a 248 * running {@code FadeTransition}, the animation has to be stopped and 249 * started again to pick up the new value. 250 */ 251 private DoubleProperty byValue; 252 private static final double DEFAULT_BY_VALUE = 0.0; 253 254 public final void setByValue(double value) { 255 if ((byValue != null) || (Math.abs(value - DEFAULT_BY_VALUE) > EPSILON)) { 256 byValueProperty().set(value); 257 } 258 } 259 260 public final double getByValue() { 261 return (byValue == null)? DEFAULT_BY_VALUE : byValue.get(); 262 } 263 264 public final DoubleProperty byValueProperty() { 265 if (byValue == null) { 266 byValue = new SimpleDoubleProperty(this, "byValue", DEFAULT_BY_VALUE); 267 } 268 return byValue; 269 } 270 271 /** 272 * The constructor of {@code FadeTransition} 273 * 274 * @param duration 275 * The duration of the {@code FadeTransition} 276 * @param node 277 * The {@code node} which opacity will be animated 278 */ 279 public FadeTransition(Duration duration, Node node) { 280 setDuration(duration); 281 setNode(node); 282 setCycleDuration(duration); 283 } 284 285 /** 286 * The constructor of {@code FadeTransition} 287 * 288 * @param duration 289 * The duration of the {@code FadeTransition} 290 */ 291 public FadeTransition(Duration duration) { 292 this(duration, null); 293 } 294 295 /** 296 * The constructor of {@code FadeTransition} 297 */ 298 public FadeTransition() { 299 this(DEFAULT_DURATION, null); 300 } 301 302 /** 303 * {@inheritDoc} 304 */ 305 @Override 306 protected void interpolate(double frac) { 307 final double newOpacity = Math.max(0.0, 308 Math.min(start + frac * delta, 1.0)); 309 cachedNode.setOpacity(newOpacity); 310 } 311 312 private Node getTargetNode() { 313 final Node node = getNode(); 314 return (node != null) ? node : getParentTargetNode(); 315 } 316 317 @Override 318 boolean impl_startable(boolean forceSync) { 319 return super.impl_startable(forceSync) 320 && ((getTargetNode() != null) || (!forceSync && (cachedNode != null))); 321 } 322 323 @Override 324 void impl_sync(boolean forceSync) { 325 super.impl_sync(forceSync); 326 if (forceSync || (cachedNode == null)) { 327 cachedNode = getTargetNode(); 328 final double _fromValue = getFromValue(); 329 final double _toValue = getToValue(); 330 start = (!Double.isNaN(_fromValue)) ? Math.max(0, 331 Math.min(_fromValue, 1)) : cachedNode.getOpacity(); 332 delta = (!Double.isNaN(_toValue)) ? _toValue - start : getByValue(); 333 if (start + delta > 1.0) { 334 delta = 1.0 - start; 335 } else if (start + delta < 0.0) { 336 delta = -start; 337 } 338 } 339 } 340 }