haxepunk/Tween.hx
package haxepunk;
import haxepunk.Signal.Signal0;
import haxepunk.ds.Maybe;
import haxepunk.utils.Ease.EaseFunction;
/**
* The type of the tween.
*/
enum TweenType
{
/**
* Default type, the tween is still available after it ended and can
* be started again with the start() method.
*/
Persist;
/** The tween will loop. */
Looping;
/** The tween will be removed after it ended. */
OneShot;
/** The tween will loop, alternating backwards and forwards */
PingPong;
}
/**
* <p>
* Base class for tweening helpers.
* A Tween is any object that interpolates something be<i>tween</i> two
* values. It does not have to be a linear path, for instance a circular
* motion.
* </p>
* <p>
* Do not use this directly, instead use the classes in haxepunk.tweens.*
* </p>
*/
class Tween
{
/** If the tween is active. */
public var active:Bool = false;
/** Whether tween is currently running forward. For TweenType.PingPong. */
public var forward:Bool = true;
/** Signal fires when tween starts */
public var onStart = new Signal0();
/** Signal fires when tween updates */
public var onUpdate = new Signal0();
/** Signal fires when tween is completed */
public var onComplete = new Signal0();
/**
* Constructor. Specify basic information about the Tween.
* @param duration Duration of the tween (in seconds).
* @param type Tween type, one of Tween.PERSIST (default), Tween.LOOPING, or Tween.ONESHOT.
* @param complete Optional function which will bind to the onComplete signal.
* @param ease Optional easer function to apply to the Tweened value.
*/
public function new(duration:Float, ?type:TweenType, ?complete:Void->Void, ?ease:EaseFunction)
{
if (duration < 0)
{
throw "Tween duration must be positive!";
}
_target = duration;
_type = type == null ? TweenType.Persist : type;
_ease = ease;
_t = 0;
if (complete != null)
{
onComplete.bind(complete);
}
}
/** @private Update function for override in subclasses */
function updateInternal() {}
/**
* Updates the Tween, called by World.
*/
@:dox(hide)
public function update(elapsed:Float)
{
var isFinished = false;
if (active)
{
_time += elapsed;
_t = percent;
if (_t > 0 && _t < 1) _ease.may(function(f) _t = f(_t));
if (_time >= _target)
{
_t = forward ? 1 : 0;
isFinished = true;
}
updateInternal();
onUpdate.invoke();
}
if (isFinished)
{
finish();
}
}
/**
* Starts the Tween, or restarts it if it's currently running.
*/
public function start()
{
_time = 0;
if (_target == 0)
{
active = false;
onComplete.invoke();
}
else
{
active = true;
onStart.invoke();
}
}
/** @private Called when the Tween completes. */
function finish()
{
switch (_type)
{
case Persist:
_time = _target;
active = false;
case Looping, PingPong:
if (_type == PingPong) forward = !forward;
start();
case OneShot:
_time = _target;
cancel();
}
onComplete.invoke();
if (_type == TweenType.OneShot)
{
onComplete.clear();
}
}
/**
* Immediately stops the Tween and removes it from its Tweener without calling the complete callback.
*/
public function cancel()
{
active = false;
if (_parent != null)
{
_parent.removeTween(this);
}
}
/** Progression of the tween, between 0 and 1. */
public var percent(get, set):Float;
function get_percent():Float return _target == 0 ? 0 : ((forward ? _time : (_target - _time)) / _target);
function set_percent(value:Float):Float return _time = _target * value;
public var scale(get, null):Float;
function get_scale():Float return _t;
var _type:TweenType;
var _ease:Maybe<EaseFunction>;
var _t:Float;
var _time:Float = 0;
var _target:Float;
var _parent:Tweener;
var _prev:Tween;
var _next:Tween;
}