Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.animation; 027 028import javafx.beans.property.ObjectProperty; 029import javafx.beans.property.ObjectPropertyBase; 030import javafx.beans.property.SimpleObjectProperty; 031import javafx.scene.Node; 032import javafx.scene.shape.Shape; 033import javafx.util.Duration; 034 035import com.sun.javafx.animation.transition.AnimationPathHelper; 036import com.sun.javafx.animation.transition.Position2D; 037import com.sun.javafx.geom.Path2D; 038import com.sun.javafx.geom.transform.BaseTransform; 039 040/** 041 * This {@code Transition} creates a path animation that spans its 042 * {@link #duration}. The translation along the path is done by updating the 043 * {@code translateX} and {@code translateY} variables of the {@code node}, and 044 * the {@code rotate} variable will get updated if {@code orientation} is set to 045 * {@code OrientationType.ORTHOGONAL_TO_TANGENT}, at regular interval. 046 * <p> 047 * The animated path is defined by the outline of a shape. 048 * 049 * <p> 050 * Code Segment Example: 051 * </p> 052 * 053 * <pre> 054 * <code> 055 * import javafx.scene.shape.*; 056 * import javafx.animation.transition.*; 057 * 058 * ... 059 * 060 * Rectangle rect = new Rectangle (100, 40, 100, 100); 061 * rect.setArcHeight(50); 062 * rect.setArcWidth(50); 063 * rect.setFill(Color.VIOLET); 064 * 065 * 066 * Path path = new Path(); 067 * path.getElements().add (new MoveTo (0f, 50f)); 068 * path.getElements().add (new CubicCurveTo (40f, 10f, 390f, 240f, 1904, 50f)); 069 * 070 * pathTransition.setDuration(Duration.millis(10000)); 071 * pathTransition.setNode(rect); 072 * pathTransition.setPath(path); 073 * pathTransition.setOrientation(OrientationType.ORTHOGONAL_TO_TANGENT); 074 * pathTransition.setCycleCount(4f); 075 * pathTransition.setAutoReverse(true); 076 * 077 * pathTransition.play(); 078 * 079 * ... 080 * 081 * </code> 082 * </pre> 083 * 084 * @see Transition 085 * @see Animation 086 * 087 */ 088public final class PathTransition extends Transition { 089 090 /** 091 * The target node of this {@code PathTransition}. 092 * <p> 093 * It is not possible to change the target {@code node} of a running 094 * {@code PathTransition}. If the value of {@code node} is changed for a 095 * running {@code PathTransition}, the animation has to be stopped and 096 * started again to pick up the new value. 097 */ 098 private ObjectProperty<Node> node; 099 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 Transition}. 122 * <p> 123 * It is not possible to change the {@code duration} of a running 124 * {@code PathTransition}. If the value of {@code duration} is changed for a 125 * running {@code PathTransition}, 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 PathTransition.this; 171 } 172 173 @Override 174 public String getName() { 175 return "duration"; 176 } 177 }; 178 } 179 return duration; 180 } 181 182 /** 183 * The shape on which outline the node should be animated. 184 * <p> 185 * It is not possible to change the {@code path} of a running 186 * {@code PathTransition}. If the value of {@code path} is changed for a 187 * running {@code PathTransition}, the animation has to be stopped and 188 * started again to pick up the new value. 189 * 190 * @defaultValue null 191 */ 192 private ObjectProperty<Shape> path; 193 private static final Shape DEFAULT_PATH = null; 194 195 public final void setPath(Shape value) { 196 if ((path != null) || (value != null /* DEFAULT_PATH */)) { 197 pathProperty().set(value); 198 } 199 } 200 201 public final Shape getPath() { 202 return (path == null)? DEFAULT_PATH : path.get(); 203 } 204 205 public final ObjectProperty<Shape> pathProperty() { 206 if (path == null) { 207 path = new SimpleObjectProperty<Shape>(this, "path", DEFAULT_PATH); 208 } 209 return path; 210 } 211 212 /** 213 * Specifies the upright orientation of {@code node} along the {@code path}. 214 */ 215 public static enum OrientationType { 216 217 /** 218 * The targeted {@code node}'s rotation matrix stays unchange along the 219 * geometric path. 220 */ 221 NONE, 222 223 /** 224 * The targeted node's rotation matrix is set to keep {@code node} 225 * perpendicular to the path's tangent along the geometric path. 226 */ 227 ORTHOGONAL_TO_TANGENT 228 } 229 230 /** 231 * Specifies the upright orientation of {@code node} along the {@code path}. 232 * The default orientation is set to {@link OrientationType#NONE}. 233 * <p> 234 * It is not possible to change the {@code orientation} of a running 235 * {@code PathTransition}. If the value of {@code orientation} is changed 236 * for a running {@code PathTransition}, the animation has to be stopped and 237 * started again to pick up the new value. 238 * 239 * @defaultValue NONE 240 */ 241 private ObjectProperty<OrientationType> orientation; 242 private static final OrientationType DEFAULT_ORIENTATION = OrientationType.NONE; 243 244 public final void setOrientation(OrientationType value) { 245 if ((orientation != null) || (!DEFAULT_ORIENTATION.equals(value))) { 246 orientationProperty().set(value); 247 } 248 } 249 250 public final OrientationType getOrientation() { 251 return (orientation == null)? OrientationType.NONE : orientation.get(); 252 } 253 254 public final ObjectProperty<OrientationType> orientationProperty() { 255 if (orientation == null) { 256 orientation = new SimpleObjectProperty<OrientationType>(this, "orientation", DEFAULT_ORIENTATION); 257 } 258 return orientation; 259 } 260 261 private boolean cachedIsNormalRequired; 262 263 private final Position2D posResult = new Position2D(); 264 private AnimationPathHelper apHelper; 265 266 /** 267 * The constructor of {@code PathTransition}. 268 * 269 * @param duration 270 * The {@link #duration} of this {@code PathTransition} 271 * @param path 272 * The {@link #path} of this {@code PathTransition} 273 * @param node 274 * The {@link #node} of this {@code PathTransition} 275 */ 276 public PathTransition(Duration duration, Shape path, Node node) { 277 setDuration(duration); 278 setPath(path); 279 setNode(node); 280 setCycleDuration(duration); 281 } 282 283 /** 284 * The constructor of {@code PathTransition}. 285 * 286 * @param duration 287 * The {@link #duration} of this {@code PathTransition} 288 * @param path 289 * The {@link #path} of this {@code PathTransition} 290 */ 291 public PathTransition(Duration duration, Shape path) { 292 this(duration, path, null); 293 } 294 295 /** 296 * The constructor of {@code PathTransition}. 297 */ 298 public PathTransition() { 299 this(DEFAULT_DURATION, null, null); 300 } 301 302 /** 303 * {@inheritDoc} 304 */ 305 @Override 306 public void interpolate(double frac) { 307 apHelper.getPosition2D(frac, cachedIsNormalRequired, posResult); 308 cachedNode.setTranslateX(posResult.x - cachedNode.impl_getPivotX()); 309 cachedNode.setTranslateY(posResult.y - cachedNode.impl_getPivotY()); 310 // Need to handle orientation if it is requested 311 if (cachedIsNormalRequired) { 312 cachedNode.setRotate(posResult.rotateAngle); 313 } 314 } 315 316 private Node getTargetNode() { 317 final Node node = getNode(); 318 return (node != null) ? node : getParentTargetNode(); 319 } 320 321 @Override 322 boolean impl_startable(boolean forceSync) { 323 return super.impl_startable(forceSync) 324 && (((getTargetNode() != null) && (getPath() != null) && !getPath().getLayoutBounds().isEmpty()) || (!forceSync 325 && (cachedNode != null) && (apHelper != null))); 326 } 327 328 @Override 329 void impl_sync(boolean forceSync) { 330 super.impl_sync(forceSync); 331 if (forceSync || (cachedNode == null)) { 332 cachedNode = getTargetNode(); 333 final Shape path = getPath(); 334 final Path2D path2D = new Path2D(path.impl_configShape()); 335 final BaseTransform tx = path.impl_getLeafTransform(); 336 apHelper = new AnimationPathHelper(path2D, tx, 1.0); 337 cachedIsNormalRequired = getOrientation() == OrientationType.ORTHOGONAL_TO_TANGENT; 338 } 339 } 340 341}