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.DoubleProperty;
029import javafx.beans.property.ObjectProperty;
030import javafx.beans.property.ObjectPropertyBase;
031import javafx.beans.property.SimpleDoubleProperty;
032import javafx.beans.property.SimpleObjectProperty;
033import javafx.scene.Node;
034import javafx.util.Duration;
035
036/**
037 * This {@code Transition} creates a fade effect animation that spans its
038 * {@code duration}. This is done by updating the {@code opacity} variable of
039 * the {@code node} at regular interval.
040 * <p>
041 * It starts from the {@code fromValue} if provided else uses the {@code node}'s
042 * {@code opacity} value.
043 * <p>
044 * It stops at the {@code toValue} value if provided else it will use start
045 * value plus {@code byValue}.
046 * <p>
047 * The {@code toValue} takes precedence if both {@code toValue} and
048 * {@code byValue} are specified.
049 * 
050 * <p>
051 * Code Segment Example:
052 * </p>
053 * 
054 * <pre>
055 * <code>
056 * import javafx.scene.shape.*;
057 * import javafx.animation.transition.*;
058 * 
059 * ...
060 * 
061 *     Rectangle rect = new Rectangle (100, 40, 100, 100);
062 *     rect.setArcHeight(50);
063 *     rect.setArcWidth(50);
064 *     rect.setFill(Color.VIOLET);
065 * 
066 *     FadeTransition ft = new FadeTransition(Duration.millis(3000), rect);
067 *     ft.setFromValue(1.0);
068 *     ft.setToValue(0.3);
069 *     ft.setCycleCount(4);
070 *     ft.setAutoReverse(true);
071 * 
072 *     ft.play();
073 * 
074 * ...
075 * 
076 * </code>
077 * </pre>
078 * 
079 * @see Transition
080 * @see Animation
081 * 
082 */
083public final class FadeTransition extends Transition {
084    private static final double EPSILON = 1e-12;
085
086    private double start;
087    private double delta;
088
089    /**
090     * The target node of this {@code Transition}.
091     * <p>
092     * It is not possible to change the target {@code node} of a running
093     * {@code FadeTransition}. If the value of {@code node} is changed for a
094     * running {@code FadeTransition}, the animation has to be stopped and
095     * started again to pick up the new value.
096     */
097    private ObjectProperty<Node> node;
098    private static final Node DEFAULT_NODE = null;
099
100    public final void setNode(Node value) {
101        if ((node != null) || (value != null /* DEFAULT_NODE */)) {
102            nodeProperty().set(value);
103        }
104    }
105
106    public final Node getNode() {
107        return (node == null)? DEFAULT_NODE : node.get();
108    }
109
110    public final ObjectProperty<Node> nodeProperty() {
111        if (node == null) {
112            node = new SimpleObjectProperty<Node>(this, "node", DEFAULT_NODE);
113        }
114        return node;
115    }
116
117    private Node cachedNode;
118
119    /**
120     * The duration of this {@code FadeTransition}.
121     * <p>
122     * It is not possible to change the {@code duration} of a running
123     * {@code FadeTransition}. If the value of {@code duration} is changed for a
124     * running {@code FadeTransition}, the animation has to be stopped and
125     * started again to pick up the new value.
126     * <p>
127     * Note: While the unit of {@code duration} is a millisecond, the
128     * granularity depends on the underlying operating system and will in
129     * general be larger. For example animations on desktop systems usually run
130     * with a maximum of 60fps which gives a granularity of ~17 ms.
131     *
132     * Setting duration to value lower than {@link Duration#ZERO} will result
133     * in {@link IllegalArgumentException}.
134     *
135     * @defaultValue 400ms
136     */
137    private ObjectProperty<Duration> duration;
138    private static final Duration DEFAULT_DURATION = Duration.millis(400);
139
140    public final void setDuration(Duration value) {
141        if ((duration != null) || (!DEFAULT_DURATION.equals(value))) {
142            durationProperty().set(value);
143        }
144    }
145
146    public final Duration getDuration() {
147        return (duration == null)? DEFAULT_DURATION : duration.get();
148    }
149
150    public final ObjectProperty<Duration> durationProperty() {
151        if (duration == null) {
152            duration = new ObjectPropertyBase<Duration>(DEFAULT_DURATION) {
153
154                @Override
155                public void invalidated() {
156                    try {
157                        setCycleDuration(getDuration());
158                    } catch (IllegalArgumentException e) {
159                        if (isBound()) {
160                            unbind();
161                        }
162                        set(getCycleDuration());
163                        throw e;
164                    }
165                }
166
167                @Override
168                public Object getBean() {
169                    return FadeTransition.this;
170                }
171
172                @Override
173                public String getName() {
174                    return "duration";
175                }
176            };
177        }
178        return duration;
179    }
180
181    /**
182     * Specifies the start opacity value for this {@code FadeTransition}.
183     * <p>
184     * It is not possible to change {@code fromValue} of a running
185     * {@code FadeTransition}. If the value of {@code fromValue} is changed for
186     * a running {@code FadeTransition}, the animation has to be stopped and
187     * started again to pick up the new value.
188     * 
189     * @defaultValue {@code Double.NaN}
190     */
191    private DoubleProperty fromValue;
192    private static final double DEFAULT_FROM_VALUE = Double.NaN;
193
194    public final void setFromValue(double value) {
195        if ((fromValue != null) || (!Double.isNaN(value) /* DEFAULT_FROM_VALUE */ )) {
196            fromValueProperty().set(value);
197        }
198    }
199
200    public final double getFromValue() {
201        return (fromValue == null)? DEFAULT_FROM_VALUE : fromValue.get();
202    }
203
204    public final DoubleProperty fromValueProperty() {
205        if (fromValue == null) {
206            fromValue = new SimpleDoubleProperty(this, "fromValue", DEFAULT_FROM_VALUE);
207        }
208        return fromValue;
209    }
210
211    /**
212     * Specifies the stop opacity value for this {@code FadeTransition}.
213     * <p>
214     * It is not possible to change {@code toValue} of a running
215     * {@code FadeTransition}. If the value of {@code toValue} is changed for a
216     * running {@code FadeTransition}, the animation has to be stopped and
217     * started again to pick up the new value.
218     * 
219     * @defaultValue {@code Double.NaN}
220     */
221    private DoubleProperty toValue;
222    private static final double DEFAULT_TO_VALUE = Double.NaN;
223
224    public final void setToValue(double value) {
225        if ((toValue != null) || (!Double.isNaN(value))) {
226            toValueProperty().set(value);
227        }
228    }
229
230    public final double getToValue() {
231        return (toValue == null)? DEFAULT_TO_VALUE : toValue.get();
232    }
233
234    public final DoubleProperty toValueProperty() {
235        if (toValue == null) {
236            toValue = new SimpleDoubleProperty(this, "toValue", DEFAULT_TO_VALUE);
237        }
238        return toValue;
239    }
240
241    /**
242     * Specifies the incremented stop opacity value, from the start, of this
243     * {@code FadeTransition}.
244     * <p>
245     * It is not possible to change {@code byValue} of a running
246     * {@code FadeTransition}. If the value of {@code byValue} is changed for a
247     * running {@code FadeTransition}, the animation has to be stopped and
248     * started again to pick up the new value.
249     */
250    private DoubleProperty byValue;
251    private static final double DEFAULT_BY_VALUE = 0.0;
252
253    public final void setByValue(double value) {
254        if ((byValue != null) || (Math.abs(value - DEFAULT_BY_VALUE) > EPSILON)) {
255            byValueProperty().set(value);
256        }
257    }
258
259    public final double getByValue() {
260        return (byValue == null)? DEFAULT_BY_VALUE : byValue.get();
261    }
262
263    public final DoubleProperty byValueProperty() {
264        if (byValue == null) {
265            byValue = new SimpleDoubleProperty(this, "byValue", DEFAULT_BY_VALUE);
266        }
267        return byValue;
268    }
269
270    /**
271     * The constructor of {@code FadeTransition}
272     * 
273     * @param duration
274     *            The duration of the {@code FadeTransition}
275     * @param node
276     *            The {@code node} which opacity will be animated
277     */
278    public FadeTransition(Duration duration, Node node) {
279        setDuration(duration);
280        setNode(node);
281        setCycleDuration(duration);
282    }
283
284    /**
285     * The constructor of {@code FadeTransition}
286     * 
287     * @param duration
288     *            The duration of the {@code FadeTransition}
289     */
290    public FadeTransition(Duration duration) {
291        this(duration, null);
292    }
293
294    /**
295     * The constructor of {@code FadeTransition}
296     */
297    public FadeTransition() {
298        this(DEFAULT_DURATION, null);
299    }
300
301    /**
302     * {@inheritDoc}
303     */
304    @Override
305    protected void interpolate(double frac) {
306        final double newOpacity = Math.max(0.0,
307                Math.min(start + frac * delta, 1.0));
308        cachedNode.setOpacity(newOpacity);
309    }
310
311    private Node getTargetNode() {
312        final Node node = getNode();
313        return (node != null) ? node : getParentTargetNode();
314    }
315
316    @Override
317    boolean impl_startable(boolean forceSync) {
318        return super.impl_startable(forceSync)
319                && ((getTargetNode() != null) || (!forceSync && (cachedNode != null)));
320    }
321
322    @Override
323    void impl_sync(boolean forceSync) {
324        super.impl_sync(forceSync);
325        if (forceSync || (cachedNode == null)) {
326            cachedNode = getTargetNode();
327            final double _fromValue = getFromValue();
328            final double _toValue = getToValue();
329            start = (!Double.isNaN(_fromValue)) ? Math.max(0,
330                    Math.min(_fromValue, 1)) : cachedNode.getOpacity();
331            delta = (!Double.isNaN(_toValue)) ? _toValue - start : getByValue();
332            if (start + delta > 1.0) {
333                delta = 1.0 - start;
334            } else if (start + delta < 0.0) {
335                delta = -start;
336            }
337        }
338    }
339}