Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2011, 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.paint.Color;
033import javafx.scene.shape.Shape;
034import javafx.util.Duration;
035
036/**
037 * This {@code Transition} creates an animation, that changes the stroke color
038 * of a shape over a {@code duration}. This is done by updating the
039 * {@code stroke} variable of the {@code shape} at regular intervals.
040 * <p>
041 * It starts from the {@code fromValue} if provided else uses the {@code shape}
042 * 's {@code stroke} value. (The {@code stroke} value has to be a
043 * {@link javafx.scene.paint.Color} in this case).
044 * <p>
045 * It stops at the {@code toValue} value.
046 * 
047 * <p>
048 * Code Segment Example:
049 * </p>
050 * 
051 * <pre>
052 * <code>
053 * import javafx.scene.shape.*;
054 * import javafx.animation.transition.*;
055 * 
056 * ...
057 * 
058 *     Rectangle rect = new Rectangle (100, 40, 100, 100);
059 *     rect.setArcHeight(50);
060 *     rect.setArcWidth(50);
061 *     rect.setFill(null);
062 * 
063 *     StrokeTransition st = new StrokeTransition(Duration.millis(3000), rect, Color.RED, Color.BLUE);
064 *     st.setCycleCount(4);
065 *     st.setAutoReverse(true);
066 * 
067 *     st.play();
068 * 
069 * ...
070 * 
071 * </code>
072 * </pre>
073 * 
074 * @see Transition
075 * @see Animation
076 * 
077 */
078public final class StrokeTransition extends Transition {
079
080    private Color start;
081    private Color end;
082
083    /**
084     * The target shape of this {@code StrokeTransition}.
085     * <p>
086     * It is not possible to change the target {@code shape} of a running
087     * {@code StrokeTransition}. If the value of {@code shape} is changed for a
088     * running {@code StrokeTransition}, the animation has to be stopped and
089     * started again to pick up the new value.
090     */
091    private ObjectProperty<Shape> shape;
092    private static final Shape DEFAULT_SHAPE = null;
093
094    public final void setShape(Shape value) {
095        if ((shape != null) || (value != null /* DEFAULT_SHAPE */)) {
096            shapeProperty().set(value);
097        }
098    }
099
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 StrokeTransition}.
115     * <p>
116     * It is not possible to change the {@code duration} of a running
117     * {@code StrokeTransition}. If the value of {@code duration} is changed for
118     * a running {@code StrokeTransition}, 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 StrokeTransition.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 StrokeTransition}.
177     * <p>
178     * It is not possible to change {@code fromValue} of a running
179     * {@code StrokeTransition}. If the value of {@code fromValue} is changed
180     * for a running {@code StrokeTransition}, the animation has to be stopped
181     * and 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 StrokeTransition}.
207     * <p>
208     * It is not possible to change {@code toValue} of a running
209     * {@code StrokeTransition}. If the value of {@code toValue} is changed for
210     * a running {@code StrokeTransition}, 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 StrokeTransition}
237         * @param duration The duration of the {@code StrokeTransition}
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 StrokeTransition(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 StrokeTransition}
253         * @param duration The duration of the {@code StrokeTransition}
254         * @param fromValue The start value of the color-animation
255         * @param toValue The end value of the color-animation
256         */
257    public StrokeTransition(Duration duration, Color fromValue, Color toValue) {
258        this(duration, null, fromValue, toValue);
259    }
260
261    /**
262     * The constructor of {@code StrokeTransition}
263     * 
264     * @param duration
265     *            The duration of the {@code StrokeTransition}
266     * @param shape
267     *            The {@code shape} which stroke paint will be animated
268     */
269    public StrokeTransition(Duration duration, Shape shape) {
270        this(duration, shape, null, null);
271    }
272
273    /**
274     * The constructor of {@code StrokeTransition}
275     * 
276     * @param duration
277     *            The duration of the {@code StrokeTransition}
278     */
279    public StrokeTransition(Duration duration) {
280        this(duration, null);
281    }
282
283    /**
284     * The constructor of {@code StrokeTransition}
285     */
286    public StrokeTransition() {
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.setStroke(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 impl_startable(boolean forceSync) {
312        if (!super.impl_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.getStroke() instanceof Color)) // fromValue
324                                                                                      // defined
325                                                                                      // or
326                                                                                      // current
327                                                                                      // stroke
328                                                                                      // paint
329                                                                                      // is
330                                                                                      // Color?
331        && (getToValue() != null)); // toValue defined?
332    }
333
334    @Override
335    void impl_sync(boolean forceSync) {
336        super.impl_sync(forceSync);
337        if (forceSync || (cachedShape == null)) {
338            cachedShape = getTargetShape();
339            final Color _fromValue = getFromValue();
340            start = (_fromValue != null) ? _fromValue : (Color) cachedShape
341                    .getStroke();
342            end = getToValue();
343        }
344    }
345}