diff options
author | Jorim Jaggi <jjaggi@google.com> | 2016-06-13 16:08:20 -0700 |
---|---|---|
committer | Jorim Jaggi <jjaggi@google.com> | 2016-06-13 16:08:20 -0700 |
commit | 8f2ef8f2d70edcfa159195e2efd23f95ff1b5789 (patch) | |
tree | 07cde1e1bc6dbd85c674155ccd882d3105a4941a /java/api/src/aurelienribon/tweenengine/BaseTween.java | |
parent | 33b2d56f020ed48d3ca07c4729e302081e658c56 (diff) | |
download | universal-tween-engine-8f2ef8f2d70edcfa159195e2efd23f95ff1b5789.tar.gz |
Mirror project
https://code.google.com/archive/p/java-universal-tween-engine/source/default/source
Bug: 28936996
Change-Id: I70de8efea08289e032149d6c84c064a252ce42c2
Diffstat (limited to 'java/api/src/aurelienribon/tweenengine/BaseTween.java')
-rwxr-xr-x | java/api/src/aurelienribon/tweenengine/BaseTween.java | 543 |
1 files changed, 543 insertions, 0 deletions
diff --git a/java/api/src/aurelienribon/tweenengine/BaseTween.java b/java/api/src/aurelienribon/tweenengine/BaseTween.java new file mode 100755 index 0000000..d8cf0e6 --- /dev/null +++ b/java/api/src/aurelienribon/tweenengine/BaseTween.java @@ -0,0 +1,543 @@ +package aurelienribon.tweenengine; + +/** + * BaseTween is the base class of Tween and Timeline. It defines the + * iteration engine used to play animations for any number of times, and in + * any direction, at any speed. + * <p/> + * + * It is responsible for calling the different callbacks at the right moments, + * and for making sure that every callbacks are triggered, even if the update + * engine gets a big delta time at once. + * + * @see Tween + * @see Timeline + * @author Aurelien Ribon | http://www.aurelienribon.com/ + */ +public abstract class BaseTween<T> { + // General + private int step; + private int repeatCnt; + private boolean isIterationStep; + private boolean isYoyo; + + // Timings + protected float delay; + protected float duration; + private float repeatDelay; + private float currentTime; + private float deltaTime; + private boolean isStarted; // true when the object is started + private boolean isInitialized; // true after the delay + private boolean isFinished; // true when all repetitions are done + private boolean isKilled; // true if kill() was called + private boolean isPaused; // true if pause() was called + + // Misc + private TweenCallback callback; + private int callbackTriggers; + private Object userData; + + // Package access + boolean isAutoRemoveEnabled; + boolean isAutoStartEnabled; + + // ------------------------------------------------------------------------- + + protected void reset() { + step = -2; + repeatCnt = 0; + isIterationStep = isYoyo = false; + + delay = duration = repeatDelay = currentTime = deltaTime = 0; + isStarted = isInitialized = isFinished = isKilled = isPaused = false; + + callback = null; + callbackTriggers = TweenCallback.COMPLETE; + userData = null; + + isAutoRemoveEnabled = isAutoStartEnabled = true; + } + + // ------------------------------------------------------------------------- + // Public API + // ------------------------------------------------------------------------- + + /** + * Builds and validates the object. Only needed if you want to finalize a + * tween or timeline without starting it, since a call to ".start()" also + * calls this method. + * + * @return The current object, for chaining instructions. + */ + public T build() { + return (T) this; + } + + /** + * Starts or restarts the object unmanaged. You will need to take care of + * its life-cycle. If you want the tween to be managed for you, use a + * {@link TweenManager}. + * + * @return The current object, for chaining instructions. + */ + public T start() { + build(); + currentTime = 0; + isStarted = true; + return (T) this; + } + + /** + * Convenience method to add an object to a manager. Its life-cycle will be + * handled for you. Relax and enjoy the animation. + * + * @return The current object, for chaining instructions. + */ + public T start(TweenManager manager) { + manager.add(this); + return (T) this; + } + + /** + * Adds a delay to the tween or timeline. + * + * @param delay A duration. + * @return The current object, for chaining instructions. + */ + public T delay(float delay) { + this.delay += delay; + return (T) this; + } + + /** + * Kills the tween or timeline. If you are using a TweenManager, this object + * will be removed automatically. + */ + public void kill() { + isKilled = true; + } + + /** + * Stops and resets the tween or timeline, and sends it to its pool, for ++ * later reuse. Note that if you use a {@link TweenManager}, this method ++ * is automatically called once the animation is finished. + */ + public void free() { + } + + /** + * Pauses the tween or timeline. Further update calls won't have any effect. + */ + public void pause() { + isPaused = true; + } + + /** + * Resumes the tween or timeline. Has no effect is it was no already paused. + */ + public void resume() { + isPaused = false; + } + + /** + * Repeats the tween or timeline for a given number of times. + * @param count The number of repetitions. For infinite repetition, + * use Tween.INFINITY, or a negative number. + * + * @param delay A delay between each iteration. + * @return The current tween or timeline, for chaining instructions. + */ + public T repeat(int count, float delay) { + if (isStarted) throw new RuntimeException("You can't change the repetitions of a tween or timeline once it is started"); + repeatCnt = count; + repeatDelay = delay >= 0 ? delay : 0; + isYoyo = false; + return (T) this; + } + + /** + * Repeats the tween or timeline for a given number of times. + * Every two iterations, it will be played backwards. + * + * @param count The number of repetitions. For infinite repetition, + * use Tween.INFINITY, or '-1'. + * @param delay A delay before each repetition. + * @return The current tween or timeline, for chaining instructions. + */ + public T repeatYoyo(int count, float delay) { + if (isStarted) throw new RuntimeException("You can't change the repetitions of a tween or timeline once it is started"); + repeatCnt = count; + repeatDelay = delay >= 0 ? delay : 0; + isYoyo = true; + return (T) this; + } + + /** + * Sets the callback. By default, it will be fired at the completion of the + * tween or timeline (event COMPLETE). If you want to change this behavior + * and add more triggers, use the {@link setCallbackTriggers()} method. + * + * @see TweenCallback + */ + public T setCallback(TweenCallback callback) { + this.callback = callback; + return (T) this; + } + + /** + * Changes the triggers of the callback. The available triggers, listed as + * members of the {@link TweenCallback} interface, are: + * <p/> + * + * <b>BEGIN</b>: right after the delay (if any)<br/> + * <b>START</b>: at each iteration beginning<br/> + * <b>END</b>: at each iteration ending, before the repeat delay<br/> + * <b>COMPLETE</b>: at last END event<br/> + * <b>BACK_BEGIN</b>: at the beginning of the first backward iteration<br/> + * <b>BACK_START</b>: at each backward iteration beginning, after the repeat delay<br/> + * <b>BACK_END</b>: at each backward iteration ending<br/> + * <b>BACK_COMPLETE</b>: at last BACK_END event + * <p/> + * + * <pre> {@code + * forward : BEGIN COMPLETE + * forward : START END START END START END + * |--------------[XXXXXXXXXX]------[XXXXXXXXXX]------[XXXXXXXXXX] + * backward: bEND bSTART bEND bSTART bEND bSTART + * backward: bCOMPLETE bBEGIN + * }</pre> + * + * @param flags one or more triggers, separated by the '|' operator. + * @see TweenCallback + */ + public T setCallbackTriggers(int flags) { + this.callbackTriggers = flags; + return (T) this; + } + + /** + * Attaches an object to this tween or timeline. It can be useful in order + * to retrieve some data from a TweenCallback. + * + * @param data Any kind of object. + * @return The current tween or timeline, for chaining instructions. + */ + public T setUserData(Object data) { + userData = data; + return (T) this; + } + + // ------------------------------------------------------------------------- + // Getters + // ------------------------------------------------------------------------- + + /** + * Gets the delay of the tween or timeline. Nothing will happen before + * this delay. + */ + public float getDelay() { + return delay; + } + + /** + * Gets the duration of a single iteration. + */ + public float getDuration() { + return duration; + } + + /** + * Gets the number of iterations that will be played. + */ + public int getRepeatCount() { + return repeatCnt; + } + + /** + * Gets the delay occuring between two iterations. + */ + public float getRepeatDelay() { + return repeatDelay; + } + + /** + * Returns the complete duration, including initial delay and repetitions. + * The formula is as follows: + * <pre> + * fullDuration = delay + duration + (repeatDelay + duration) * repeatCnt + * </pre> + */ + public float getFullDuration() { + if (repeatCnt < 0) return -1; + return delay + duration + (repeatDelay + duration) * repeatCnt; + } + + /** + * Gets the attached data, or null if none. + */ + public Object getUserData() { + return userData; + } + + /** + * Gets the id of the current step. Values are as follows:<br/> + * <ul> + * <li>even numbers mean that an iteration is playing,<br/> + * <li>odd numbers mean that we are between two iterations,<br/> + * <li>-2 means that the initial delay has not ended,<br/> + * <li>-1 means that we are before the first iteration,<br/> + * <li>repeatCount*2 + 1 means that we are after the last iteration + */ + public int getStep() { + return step; + } + + /** + * Gets the local time. + */ + public float getCurrentTime() { + return currentTime; + } + + /** + * Returns true if the tween or timeline has been started. + */ + public boolean isStarted() { + return isStarted; + } + + /** + * Returns true if the tween or timeline has been initialized. Starting + * values for tweens are stored at initialization time. This initialization + * takes place right after the initial delay, if any. + */ + public boolean isInitialized() { + return isInitialized; + } + + /** + * Returns true if the tween is finished (i.e. if the tween has reached + * its end or has been killed). If you don't use a TweenManager, you may + * want to call {@link free()} to reuse the object later. + */ + public boolean isFinished() { + return isFinished || isKilled; + } + + /** + * Returns true if the iterations are played as yoyo. Yoyo means that + * every two iterations, the animation will be played backwards. + */ + public boolean isYoyo() { + return isYoyo; + } + + /** + * Returns true if the tween or timeline is currently paused. + */ + public boolean isPaused() { + return isPaused; + } + + // ------------------------------------------------------------------------- + // Abstract API + // ------------------------------------------------------------------------- + + protected abstract void forceStartValues(); + protected abstract void forceEndValues(); + + protected abstract boolean containsTarget(Object target); + protected abstract boolean containsTarget(Object target, int tweenType); + + // ------------------------------------------------------------------------- + // Protected API + // ------------------------------------------------------------------------- + + protected void initializeOverride() { + } + + protected void updateOverride(int step, int lastStep, boolean isIterationStep, float delta) { + } + + protected void forceToStart() { + currentTime = -delay; + step = -1; + isIterationStep = false; + if (isReverse(0)) forceEndValues(); + else forceStartValues(); + } + + protected void forceToEnd(float time) { + currentTime = time - getFullDuration(); + step = repeatCnt*2 + 1; + isIterationStep = false; + if (isReverse(repeatCnt*2)) forceStartValues(); + else forceEndValues(); + } + + protected void callCallback(int type) { + if (callback != null && (callbackTriggers & type) > 0) callback.onEvent(type, this); + } + + protected boolean isReverse(int step) { + return isYoyo && Math.abs(step%4) == 2; + } + + protected boolean isValid(int step) { + return (step >= 0 && step <= repeatCnt*2) || repeatCnt < 0; + } + + protected void killTarget(Object target) { + if (containsTarget(target)) kill(); + } + + protected void killTarget(Object target, int tweenType) { + if (containsTarget(target, tweenType)) kill(); + } + + // ------------------------------------------------------------------------- + // Update engine + // ------------------------------------------------------------------------- + + /** + * Updates the tween or timeline state. <b>You may want to use a + * TweenManager to update objects for you.</b> + * + * Slow motion, fast motion and backward play can be easily achieved by + * tweaking this delta time. Multiply it by -1 to play the animation + * backward, or by 0.5 to play it twice slower than its normal speed. + * + * @param delta A delta time between now and the last call. + */ + public void update(float delta) { + if (!isStarted || isPaused || isKilled) return; + + deltaTime = delta; + + if (!isInitialized) { + initialize(); + } + + if (isInitialized) { + testRelaunch(); + updateStep(); + testCompletion(); + } + + currentTime += deltaTime; + deltaTime = 0; + } + + private void initialize() { + if (currentTime+deltaTime >= delay) { + initializeOverride(); + isInitialized = true; + isIterationStep = true; + step = 0; + deltaTime -= delay-currentTime; + currentTime = 0; + callCallback(TweenCallback.BEGIN); + callCallback(TweenCallback.START); + } + } + + private void testRelaunch() { + if (!isIterationStep && repeatCnt >= 0 && step < 0 && currentTime+deltaTime >= 0) { + assert step == -1; + isIterationStep = true; + step = 0; + float delta = 0-currentTime; + deltaTime -= delta; + currentTime = 0; + callCallback(TweenCallback.BEGIN); + callCallback(TweenCallback.START); + updateOverride(step, step-1, isIterationStep, delta); + + } else if (!isIterationStep && repeatCnt >= 0 && step > repeatCnt*2 && currentTime+deltaTime < 0) { + assert step == repeatCnt*2 + 1; + isIterationStep = true; + step = repeatCnt*2; + float delta = 0-currentTime; + deltaTime -= delta; + currentTime = duration; + callCallback(TweenCallback.BACK_BEGIN); + callCallback(TweenCallback.BACK_START); + updateOverride(step, step+1, isIterationStep, delta); + } + } + + private void updateStep() { + while (isValid(step)) { + if (!isIterationStep && currentTime+deltaTime <= 0) { + isIterationStep = true; + step -= 1; + + float delta = 0-currentTime; + deltaTime -= delta; + currentTime = duration; + + if (isReverse(step)) forceStartValues(); else forceEndValues(); + callCallback(TweenCallback.BACK_START); + updateOverride(step, step+1, isIterationStep, delta); + + } else if (!isIterationStep && currentTime+deltaTime >= repeatDelay) { + isIterationStep = true; + step += 1; + + float delta = repeatDelay-currentTime; + deltaTime -= delta; + currentTime = 0; + + if (isReverse(step)) forceEndValues(); else forceStartValues(); + callCallback(TweenCallback.START); + updateOverride(step, step-1, isIterationStep, delta); + + } else if (isIterationStep && currentTime+deltaTime < 0) { + isIterationStep = false; + step -= 1; + + float delta = 0-currentTime; + deltaTime -= delta; + currentTime = 0; + + updateOverride(step, step+1, isIterationStep, delta); + callCallback(TweenCallback.BACK_END); + + if (step < 0 && repeatCnt >= 0) callCallback(TweenCallback.BACK_COMPLETE); + else currentTime = repeatDelay; + + } else if (isIterationStep && currentTime+deltaTime > duration) { + isIterationStep = false; + step += 1; + + float delta = duration-currentTime; + deltaTime -= delta; + currentTime = duration; + + updateOverride(step, step-1, isIterationStep, delta); + callCallback(TweenCallback.END); + + if (step > repeatCnt*2 && repeatCnt >= 0) callCallback(TweenCallback.COMPLETE); + currentTime = 0; + + } else if (isIterationStep) { + float delta = deltaTime; + deltaTime -= delta; + currentTime += delta; + updateOverride(step, step, isIterationStep, delta); + break; + + } else { + float delta = deltaTime; + deltaTime -= delta; + currentTime += delta; + break; + } + } + } + + private void testCompletion() { + isFinished = repeatCnt >= 0 && (step > repeatCnt*2 || step < 0); + } +} |