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