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 com.sun.scenario.animation.AbstractMasterTimer;
029import javafx.beans.property.ObjectProperty;
030import javafx.beans.property.SimpleObjectProperty;
031import javafx.scene.Node;
032
033/**
034 * An abstract class that contains the basic functionalities required by all
035 * {@code Transition} based animations, such as {@link PathTransition} and
036 * {@link RotateTransition}.
037 * <p>
038 * This class offers a simple framework to define animation. It provides all the
039 * basic functionality defined in {@link Animation}. {@code Transition} requires
040 * the implementation of a method {@link #interpolate(double)} which is the
041 * called in each frame, while the {@code Transition} is running.
042 * <p>
043 * In addition an extending class needs to set the duration of a single cycle
044 * with {@link Animation#setCycleDuration(javafx.util.Duration)}. This duration
045 * is usually set by the user via a duration property (as in
046 * {@link FadeTransition#duration}) for example. But it can also be calculated
047 * by the extending class as is done in {@link ParallelTransition} and
048 * {@link FadeTransition}.
049 * <p>
050 * Below is a simple example. It creates a small animation that updates the
051 * {@code text} property of a {@link javafx.scene.text.Text} node. It starts
052 * with an empty {@code String} and adds gradually letter by letter until the
053 * full {@code String} was set when the animation finishes.
054 * 
055 * <pre>
056 * {@code
057 * 
058 * final String content = "Lorem ipsum";
059 * final Text text = new Text(10, 20, "");
060 * 
061 * final Animation animation = new Transition() {
062 *     {
063 *         setCycleDuration(Duration.millis(2000));
064 *     }
065 * 
066 *     protected void interpolate(double frac) {
067 *         final int length = content.length();
068 *         final int n = Math.round(length * (float) frac);
069 *         text.setText(content.substring(0, n));
070 *     }
071 * 
072 * };
073 * 
074 * animation.play();
075 * }</pre>
076 * 
077 * @see Animation
078 * 
079 */
080public abstract class Transition extends Animation {
081
082    /**
083     * Controls the timing for acceleration and deceleration at each
084     * {@code Transition} cycle.
085     * <p>
086     * This may only be changed prior to starting the transition or after the
087     * transition has ended. If the value of {@code interpolator} is changed for
088     * a running {@code Transition}, the animation has to be stopped and started again to
089     * pick up the new value.
090     * <p>
091     * Default interpolator is set to {@link Interpolator#EASE_BOTH}.
092     * 
093     * @defaultValue EASE_BOTH
094     */
095    private ObjectProperty<Interpolator> interpolator;
096    private static final Interpolator DEFAULT_INTERPOLATOR = Interpolator.EASE_BOTH;
097
098    public final void setInterpolator(Interpolator value) {
099        if ((interpolator != null) || (!DEFAULT_INTERPOLATOR.equals(value))) {
100            interpolatorProperty().set(value);
101        }
102    }
103
104    public final Interpolator getInterpolator() {
105        return (interpolator == null) ? DEFAULT_INTERPOLATOR : interpolator.get();
106    }
107
108    public final ObjectProperty<Interpolator> interpolatorProperty() {
109        if (interpolator == null) {
110            interpolator = new SimpleObjectProperty<Interpolator>(
111                    this, "interpolator", DEFAULT_INTERPOLATOR
112            );
113        }
114        return interpolator;
115    }
116
117    private Interpolator cachedInterpolator;
118
119    /**
120     * Returns the {@link Interpolator}, that was set when the
121     * {@code Transition} was started.
122     * 
123     * Changing the {@link #interpolator} of a running {@code Transition} should
124     * have no immediate effect. Instead the running {@code Transition} should
125     * continue to use the original {@code Interpolator} until it is stopped and
126     * started again.
127     * 
128     * @return the {@code Interpolator} that was set when this
129     *         {@code Transition} was started
130     */
131    protected Interpolator getCachedInterpolator() {
132        return cachedInterpolator;
133    }
134
135    /**
136     * The constructor of {@code Transition}.
137     * 
138     * This constructor allows to define a {@link #targetFramerate}.
139     * 
140     * @param targetFramerate
141     *            The custom target frame rate for this {@code Transition}
142     */
143    public Transition(double targetFramerate) {
144        super(targetFramerate);
145    }
146
147    /**
148     * The constructor of {@code Transition}.
149     */
150    public Transition() {
151    }
152
153    // For testing purposes
154    Transition(AbstractMasterTimer timer) {
155        super(timer);
156    }
157
158    /**
159     * Returns the target {@link Node} for animation of this {@code Transition}.
160     * This method returns {@code node} if it is set, else returns its
161     * {@code parent.getTargetNode()} otherwise null.
162     */
163    protected Node getParentTargetNode() {
164        return (parent != null && parent instanceof Transition) ?
165                ((Transition)parent).getParentTargetNode() : null;
166    }
167
168    /**
169     * The method {@code interpolate()} has to be provided by implementations of
170     * {@code Transition}. While a {@code Transition} is running, this method is
171     * called in every frame.
172     * 
173     * The parameter defines the current position with the animation. At the
174     * start, the fraction will be {@code 0.0} and at the end it will be
175     * {@code 1.0}. How the parameter increases, depends on the
176     * {@link #interpolatorProperty() interpolator}, e.g. if the
177     * {@code interpolator} is {@link Interpolator#LINEAR}, the fraction will
178     * increase linear.
179     * 
180     * This method must not be called by the user directly.
181     * 
182     * @param frac
183     *            The relative position
184     */
185    protected abstract void interpolate(double frac);
186
187    private double calculateFraction(long currentTicks, long cycleTicks) {
188        final double frac = (double) currentTicks / cycleTicks;
189        return cachedInterpolator.interpolate(0.0, 1.0, frac);
190    }
191
192    @Override
193    boolean impl_startable(boolean forceSync) {
194        return super.impl_startable(forceSync)
195                && ((getInterpolator() != null) || (!forceSync && (cachedInterpolator != null)));
196    }
197
198    @Override
199    void impl_sync(boolean forceSync) {
200        super.impl_sync(forceSync);
201        if (forceSync || (cachedInterpolator == null)) {
202            cachedInterpolator = getInterpolator();
203        }
204    }
205
206    @Override
207    void impl_playTo(long currentTicks, long cycleTicks) {
208        impl_setCurrentTicks(currentTicks);
209        interpolate(calculateFraction(currentTicks, cycleTicks));
210    }
211
212    @Override
213    void impl_jumpTo(long currentTicks, long cycleTicks, boolean forceJump) {
214        impl_setCurrentTicks(currentTicks);
215        if (getStatus() != Status.STOPPED || forceJump) {
216            impl_sync(false);
217            interpolate(calculateFraction(currentTicks, cycleTicks));
218        }
219    }
220}