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

swingtree.animation.Animator Maven / Gradle / Ivy

package swingtree.animation;

import org.jspecify.annotations.Nullable;
import swingtree.ComponentDelegate;

import java.awt.*;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

/**
 *  An API for creating an {@link Animation} and defining how it should be executed.
 *  Instances of this class are intended to be created and used either by the
 *  {@link swingtree.UI} API or the user event delegation API (see {@link ComponentDelegate}). 
* The UI API can be used like so: *
{@code
 *    UI.schedule( 100, TimeUnit.MILLISECONDS ) // returns an Animate instance
 *       .until( it -> it.progress() >= 0.75 && someOtherCondition() )
 *       .go( state -> {
 *          // do something
 *          someComponent.setValue( it.progress() );
 *          // ...
 *          someComponent.repaint();
 *       });
 *   }
* The user event delegation API can be used like this: *
{@code
 *       panel()
 *       .onMouseClick( it -> {
 *           it.animateFor( 100, TimeUnit.MILLISECONDS )
 *           .goOnce( state -> {
 *               int width = (int) (100 * state.progress());
 *               it.getComponent().setSize( width, 100 );
 *           });
 *       })
 *   }
*/ public class Animator { private final LifeTime _lifeTime; // Never null private final Stride _stride; // Never null private final @Nullable Component _component; // may be null private final @Nullable RunCondition _condition; // may be null /** * Creates an {@link Animator} instance which allows you to define the stop condition * for an animation as well as an {@link Animation} that will be executed * when passed to the {@link #go(Animation)} method. * * @param lifeTime The schedule that defines when the animation should be executed and for how long. * @return An {@link Animator} instance that can be used to define how the animation should be executed. */ public static Animator animateFor( LifeTime lifeTime ) { return animateFor( lifeTime, Stride.PROGRESSIVE ); } /** * Creates an {@link Animator} instance which allows you to define the stop condition * for an animation as well as an {@link Animation} that will be executed * when passed to the {@link #go(Animation)} method. * * @param lifeTime The schedule that defines when the animation should be executed and for how long. * @param stride The stride of the animation, i.e. whether it should be executed progressively or regressively. * @return An {@link Animator} instance that can be used to define how the animation should be executed. */ public static Animator animateFor( LifeTime lifeTime, Stride stride ) { return new Animator( lifeTime, stride, null, null ); } /** * Creates an {@link Animator} instance which allows you to define the stop condition * for an animation as well as an {@link Animation} that will be executed * when passed to the {@link #go(Animation)} method. * * @param lifeTime The schedule that defines when the animation should be executed and for how long. * @param component The component that should be repainted after each animation step. * @return An {@link Animator} instance that can be used to define how the animation should be executed. */ public static Animator animateFor( LifeTime lifeTime, Component component ) { return animateFor( lifeTime, Stride.PROGRESSIVE, component ); } /** * Creates an {@link Animator} instance which allows you to define the stop condition * for an animation as well as an {@link Animation} that will be executed * when passed to the {@link #go(Animation)} method. * * @param lifeTime The schedule that defines when the animation should be executed and for how long. * @param stride The stride of the animation, i.e. whether it should be executed progressively or regressively. * See {@link Stride} for more information. * @param component The component that should be repainted after each animation step. * @return An {@link Animator} instance that can be used to define how the animation should be executed. */ public static Animator animateFor( LifeTime lifeTime, Stride stride, Component component ) { return new Animator( lifeTime, stride, component, null ); } private Animator( LifeTime lifeTime, Stride stride, @Nullable Component component, @Nullable RunCondition animation ) { _lifeTime = Objects.requireNonNull(lifeTime); _stride = Objects.requireNonNull(stride); _component = component; // may be null _condition = animation; // may be null } /** * Use this to define a stop condition for the animation. * * @param shouldStop The stop condition for the animation, i.e. the animation will be executed * until this condition is true. * @return A new {@link Animator} instance that will be executed until the given stop condition is true. */ public Animator until( Predicate shouldStop ) { return this.asLongAs( shouldStop.negate() ); } /** * Use this to define a running condition for the animation. * * @param shouldRun The running condition for the animation, i.e. the animation will be executed * as long as this condition is true. * @return A new {@link Animator} instance that will be executed as long as the given running condition is true. */ public Animator asLongAs( Predicate shouldRun ) { return new Animator(_lifeTime, _stride, _component, state -> { if ( shouldRun.test(state) ) return _condition == null || _condition.shouldContinue(state); return false; }); } /** * Runs the given animation based on the stop condition defined by {@link #until(Predicate)} or {@link #asLongAs(Predicate)}. * If no stop condition was defined, the animation will be executed once. * If you want to run an animation forever, simply pass {@code state -> true} to * the {@link #asLongAs(Predicate)} method, or {@code state -> false} to the {@link #until(Predicate)} method. * * @param animation The animation that should be executed. */ public void go( Animation animation ) { RunCondition shouldRun = Optional.ofNullable(_condition).orElse( state -> state.repeats() == 0 ); AnimationRunner.add( new ComponentAnimator( _component, LifeSpan.startingNowWith(Objects.requireNonNull(_lifeTime)), _stride, shouldRun, animation )); } /** * Runs the given animation based on a time offset in the given time unit * and the stop condition defined by {@link #until(Predicate)} or {@link #asLongAs(Predicate)}. * If no stop condition was defined, the animation will be executed once. * If you want to run an animation forever, simply pass {@code state -> true} to * the {@link #asLongAs(Predicate)} method, or {@code state -> false} to the {@link #until(Predicate)} method. *

* This method is useful in cases where you want an animation to start in the future, * or somewhere in the middle of their lifespan progress (see {@link AnimationState#progress()}). * * @param offset The offset in the given time unit after which the animation should be executed. * This number may also be negative, in which case the animation will be executed * immediately, and with a {@link AnimationState#progress()} value that is * advanced according to the offset. * * @param unit The time unit in which the offset is specified. * @param animation The animation that should be executed. */ public void goWithOffset( long offset, TimeUnit unit, Animation animation ) { RunCondition shouldRun = Optional.ofNullable(_condition).orElse( state -> state.repeats() == 0 ); AnimationRunner.add( new ComponentAnimator( _component, LifeSpan.startingNowWithOffset(offset, unit, Objects.requireNonNull(_lifeTime)), _stride, shouldRun, animation )); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy