All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.animation.client.Animation Maven / Gradle / Ivy

/*
 * Copyright 2008 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.animation.client;

import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback;
import com.google.gwt.animation.client.AnimationScheduler.AnimationHandle;
import com.google.gwt.core.client.Duration;
import com.google.gwt.dom.client.Element;

/**
 * An {@link Animation} is a continuous event that updates progressively over
 * time at a non-fixed frame rate.
 */
public abstract class Animation {

  private final AnimationCallback callback = new AnimationCallback() {
    @Override
    public void execute(double timestamp) {
      if (update(timestamp)) {
        // Schedule the next animation frame.
        requestHandle = scheduler.requestAnimationFrame(callback, element);
      } else {
        requestHandle = null;
      }
    }
  };

  /**
   * The duration of the {@link Animation} in milliseconds.
   */
  private int duration = -1;

  /**
   * The element being animated.
   */
  private Element element;

  /**
   * Is the animation running, even if it hasn't started yet.
   */
  private boolean isRunning = false;

  /**
   * Has the {@link Animation} actually started.
   */
  private boolean isStarted = false;

  /**
   * The ID of the pending animation request.
   */
  private AnimationHandle requestHandle;

  /**
   * The unique ID of the current run. Used to handle cases where an animation
   * is restarted within an execution block.
   */
  private int runId = -1;

  private final AnimationScheduler scheduler;

  /**
   * The start time of the {@link Animation}.
   */
  private double startTime = -1;

  /**
   * Did the animation start before {@link #cancel()} was called.
   */
  private boolean wasStarted = false;

  /**
   * Construct a new {@link Animation}.
   */
  public Animation() {
    this(AnimationScheduler.get());
  }

  /**
   * Construct a new {@link AnimationScheduler} using the specified scheduler to
   * sheduler request frames.
   * 
   * @param scheduler an {@link AnimationScheduler} instance
   */
  protected Animation(AnimationScheduler scheduler) {
    this.scheduler = scheduler;
  }

  /**
   * Immediately cancel this animation. If the animation is running or is
   * scheduled to run, {@link #onCancel()} will be called.
   */
  public void cancel() {
    // Ignore if the animation is not currently running.
    if (!isRunning) {
      return;
    }

    // Reset the state.
    wasStarted = isStarted; // Used by onCancel.
    element = null;
    isRunning = false;
    isStarted = false;

    // Cancel the animation request.
    if (requestHandle != null) {
      requestHandle.cancel();
      requestHandle = null;
    }

    onCancel();
  }

  /**
   * Immediately run this animation. If the animation is already running, it
   * will be canceled first.
   * 

* This is equivalent to run(duration, null). * * @param duration the duration of the animation in milliseconds * @see #run(int, Element) */ public void run(int duration) { run(duration, null); } /** * Immediately run this animation. If the animation is already running, it * will be canceled first. *

* If the element is not null, the {@link #onUpdate(double)} * method might be called only if the element may be visible (generally left * at the appreciation of the browser). Otherwise, it will be called * unconditionally. * * @param duration the duration of the animation in milliseconds * @param element the element that visually bounds the entire animation */ public void run(int duration, Element element) { run(duration, Duration.currentTimeMillis(), element); } /** * Run this animation at the given startTime. If the startTime has already * passed, the animation will run synchronously as if it started at the * specified start time. If the animation is already running, it will be * canceled first. *

* This is equivalent to run(duration, startTime, null). * * @param duration the duration of the animation in milliseconds * @param startTime the synchronized start time in milliseconds * @see #run(int, double, Element) */ public void run(int duration, double startTime) { run(duration, startTime, null); } /** * Run this animation at the given startTime. If the startTime has already * passed, the animation will run synchronously as if it started at the * specified start time. If the animation is already running, it will be * canceled first. *

* If the element is not null, the {@link #onUpdate(double)} * method might be called only if the element may be visible (generally left * at the appreciation of the browser). Otherwise, it will be called * unconditionally. * * @param duration the duration of the animation in milliseconds * @param startTime the synchronized start time in milliseconds * @param element the element that visually bounds the entire animation */ public void run(int duration, double startTime, Element element) { // Cancel the animation if it is running cancel(); // Save the duration and startTime isRunning = true; isStarted = false; this.duration = duration; this.startTime = startTime; this.element = element; ++runId; // Execute the first callback. callback.execute(Duration.currentTimeMillis()); } /** * Returns true if the animation is running. * Note that animation may be 'running' but no callbacks is executed yet. */ public boolean isRunning() { return isRunning; } /** * Interpolate the linear progress into a more natural easing function. * * Depending on the {@link Animation}, the return value of this method can be * less than 0.0 or greater than 1.0. * * @param progress the linear progress, between 0.0 and 1.0 * @return the interpolated progress */ protected double interpolate(double progress) { return (1 + Math.cos(Math.PI + progress * Math.PI)) / 2; } /** * Called immediately after the animation is canceled. The default * implementation of this method calls {@link #onComplete()} only if the * animation has actually started running. */ protected void onCancel() { if (wasStarted) { onComplete(); } } /** * Called immediately after the animation completes. */ protected void onComplete() { onUpdate(interpolate(1.0)); } /** * Called immediately before the animation starts. */ protected void onStart() { onUpdate(interpolate(0.0)); } /** * Called when the animation should be updated. * * The value of progress is between 0.0 and 1.0 (inclusive) (unless you * override the {@link #interpolate(double)} method to provide a wider range * of values). There is no guarantee that {@link #onUpdate(double)} is called * with 0.0 or 1.0. * If you need to perform setup or tear down procedures, you can override * {@link #onStart()} and {@link #onComplete()}. * * @param progress a double, normally between 0.0 and 1.0 (inclusive) */ protected abstract void onUpdate(double progress); /** * Check if the specified run ID is still being run. * * @param curRunId the current run ID to check * @return true if running, false if canceled or restarted */ private boolean isRunning(int curRunId) { return isRunning && (runId == curRunId); } /** * Update the {@link Animation}. * * @param curTime the current time * @return true if the animation should run again, false if it is complete */ private boolean update(double curTime) { /* * Save the run id. If the runId is incremented during this execution block, * we know that this run has been canceled. */ final int curRunId = runId; boolean finished = curTime >= startTime + duration; if (isStarted && !finished) { // Animation is in progress. double progress = (curTime - startTime) / duration; onUpdate(interpolate(progress)); return isRunning(curRunId); // Check if this run was canceled. } if (!isStarted && curTime >= startTime) { /* * Start the animation. We do not call onUpdate() because onStart() calls * onUpdate() by default. */ isStarted = true; onStart(); if (!isRunning(curRunId)) { // This run was canceled. return false; } // Intentional fall through to possibly end the animation. } if (finished) { // Animation is complete. isRunning = false; isStarted = false; onComplete(); return false; } return true; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy