package org.as3lib.kitchensync.action
{
import flash.errors.IllegalOperationError;
import org.as3lib.kitchensync.KitchenSyncDefaults;
import org.as3lib.kitchensync.action.tweentarget.ITweenTarget;
import org.as3lib.kitchensync.action.tweentarget.TargetProperty;
import org.as3lib.kitchensync.core.*;
import org.as3lib.kitchensync.easing.EasingUtil;
import org.as3lib.kitchensync.utils.*;
/**
* A tween will change an object's numeric value over time.
* Uses a TweenTarget object to determine what to tween. This can be handled automatically
* or declared explicitly.
* Rule of thumb: lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween is the action that handles the timing and starting and stopping
* the tween while ITweenTargets control the values of the tween.
*
* @see org.as3lib.kitchensync.action.tweentarget.ITweenTarget
* @see org.as3lib.kitchensync.action.KSSimpleAction
* @since 0.1
* @author Mims Wright
*/
public class lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween extends AbstractAction implements ITween
{
/**
* Use this property to cause the tween to start from whatever the targetProperty is
* set to at the time the tween executes.
*/
public static const VALUE_AT_START_OF_TWEEN:Number = NaN;
/**
* The function used to interpolated the values between the start and end points.
*
* @see org.as3lib.kitchensync.easing
*/
public function get easingFunction():Function { return _easingFunction; }
public function set easingFunction(easingFunction:Function):void{ _easingFunction = easingFunction;}
protected var _easingFunction:Function;
/**
* A list of tween targets used by this tween.
*
* @see org.as3lib.kitchensync.action.tweentarget.ITweenTarget
* @see org.as3lib.kitchensync.action.tweentarget.TargetProperty
*/
protected var _tweenTargets:Array;
public function get tweenTargets():Array { return _tweenTargets; }
public function addTweenTarget(tweenTarget:ITweenTarget):void { _tweenTargets.push(tweenTarget); }
public function removeTweenTarget(tweenTarget:ITweenTarget):void {
var index:int = _tweenTargets.indexOf(tweenTarget);
if (index >= 0) {
_tweenTargets.splice(index, 1);
}
}
public function removeAllTweenTargets():void {
_tweenTargets = new Array();
}
/**
* Used to modify the results of the easing function.
* This is only used on some functions such as Elastic.
*/
public function get easingMod1():Number { return _easingMod1; }
public function set easingMod1(easingMod1:Number):void { _easingMod1 = easingMod1; }
protected var _easingMod1:Number;
/**
* Used to modify the results of the easing function.
* This is only used on some functions such as Elastic.
*/
public function get easingMod2():Number { return _easingMod2; }
public function set easingMod2(easingMod2:Number):void { _easingMod2 = easingMod2; }
protected var _easingMod2:Number;
/**
* Indicates whether the final value for the easing function should snap to the
* target _toValue. If set to true, the target property will equal _toValue regardless
* of the results of the easing function.
*
* @default true
*/
public function get snapToValueOnComplete():Boolean { return _snapToValueOnComplete; }
public function set snapToValueOnComplete(snapToValueOnComplete:Boolean):void { _snapToValueOnComplete = snapToValueOnComplete; }
protected var _snapToValueOnComplete:Boolean;
/**
* Indicates whether tweened values should snap to whole value numbers or use decimals.
* If set to true, the results of the easing functions on the target property will be
* rounded to the nearest integer.
*
* @see org.as3lib.kitchensync.ActionDefaults
// todo rename to snapToInteger
public function get snapToWholeNumber():Boolean { return _snapToWholeNumber; }
public function set snapToWholeNumber(snapToWholeNumber:Boolean):void { _snapToWholeNumber = snapToWholeNumber; }
protected var _snapToWholeNumber:Boolean;
*/
/**
* Constructor.
*
* @see #newWithTweenTarget()
*
* @param target - the object whose property will be changed (or an ITweenTarget, but it would be better to use newWithTweenTarget)
* @param property - the name of the property to change. The property must be a Number, int or uint such as a Sprite object's "alpha"
* @param startValue - the value to tween the property to. After the tween is done, this will be the value of the property.
* @param endValue - the starting value of the tween. By default, this is the value of the property before the tween begins.
* @param duration - the time in milliseconds that this tween will take to execute. String values are acceptable too.
* @param delay - the time to wait in milliseconds before starting the tween. String values are acceptable too.
* @param easingFunction - the function to use to interpolate the values between fromValue and toValue.
*/
public function lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween(target:Object = null, property:String = "", startValue:Number = VALUE_AT_START_OF_TWEEN, endValue:Number = 0, duration:* = 0, delay:* = 0, easingFunction:Function = null)
{
super();
_tweenTargets = new Array();
// If target is a tweenTarget...
if (target is ITweenTarget) {
// use the tweenTarget and ignore the first four params.
// (it's recommended that you use newWithTweenTarget() instead)
addTweenTarget(ITweenTarget(target));
} else if (target is Array) {
// add the items in the array to the tweenTargets list and ignore the rest of the params.
var tweenTargetArray:Array = target as Array;
for each (var tweenTarget:ITweenTarget in tweenTargetArray) {
addTweenTarget(tweenTarget);
}
} else if (target != null) {
// otherwise, create a TargetProperty object.
addTweenTarget(new TargetProperty(target, property, startValue, endValue));
}
snapToValueOnComplete = KitchenSyncDefaults.snapToValueOnComplete;
//note: moved to ITweenTarget
//snapToWholeNumber = KitchenSyncDefaults.snapToWholeNumber;
this.duration = duration;
this.delay = delay;
if (easingFunction == null) {
easingFunction = KitchenSyncDefaults.easingFunction;
}
_easingFunction = easingFunction;
}
/**
* Alternative constructor: creates a new lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween using an ITweenTarget that you pass into it.
*
* @param tweenTarget An explicitly defined tweenTarget object (or an array of tweentargets) that contains the values you want to tween.
* @param duration - the time in frames that this tween will take to execute.
* @param delay - the time to wait before starting the tween.
* @param easingFunction - the function to use to interpolate the values between fromValue and toValue.
* @return A new lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween object.
*/
public static function newWithTweenTarget(tweenTarget:*, duration:* = 0, delay:* = 0, easingFunction:Function = null):lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween {
if (tweenTarget is Array || tweenTarget is ITweenTarget) {
return new lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween(tweenTarget, "", NaN, NaN, duration, delay, easingFunction);
}
// else
throw new TypeError("'tweenTarget' parameter must be of type ITweenTarget or of type Array (containting ITweenTarget).");
}
/**
* Starts the Tween.
*
* @returns A reference to this tween.
*/
override public function start():IAction {
if (_tweenTargets && _tweenTargets.length >= 0) {
return super.start();
}
// else
throw new Error("Tween must have at least one tween target. use addTweenTarget().");
return null;
}
/**
* Stops the tween and sets the target property to the start value.
*/
public function reset():void {
stop();
for each (var target:ITweenTarget in _tweenTargets) {
target.reset();
}
}
/**
* Executes the tween.
*/
override public function update(currentTimestamp:Timestamp):void {
var timeElapsed:int;
var convertedDuration:int;
// if the tween is running and the delay time has elapsed, perform tweening.
if (startTimeHasElapsed) {
// if sync is true...
if (_sync) {
// use the actual time elapsed...
timeElapsed = currentTimestamp.currentTime - _startTime.currentTime - _delay;
convertedDuration = duration;
} else {
// rather than the number of cycles that have passed since the tween began.
timeElapsed = currentTimestamp.currentFrame - _startTime.currentFrame - TimestampUtil.millisecondsToFrames(_delay);
convertedDuration = TimestampUtil.millisecondsToFrames(duration);
}
var target:ITweenTarget
// if this is the start of the tween.
if (timeElapsed <= 1) {
for each (target in _tweenTargets) {
// if using the 'existing from value' set the start value at the time that the tween begins.
if (target.startValue == VALUE_AT_START_OF_TWEEN) {
target.startValue = target.currentValue;
}
}
}
// invoke the easing function.
var result:Number = EasingUtil.call(_easingFunction, timeElapsed, convertedDuration, _easingMod1, _easingMod2);
// apply the result to each tween target
for each (target in _tweenTargets) {
// total change in values for the tween.
//var delta:Number = target.endValue - target.startValue;
// set the tweenTarget's value.
target.updateTween(result);
}
// if the tween's duration is complete.
if (durationHasElapsed) {
// if snapToValue is set to true, the target property will be set to the target value
// regardless of the results of the easing function.
if (_snapToValueOnComplete) {
for each (target in _tweenTargets) {
target.updateTween(1.0);
}
}
// end the tween.
complete();
}
}
}
/**
* Moves the playhead to a specified time in the action. If this method is called while the
* action is paused, it will not appear to jump until after the action is unpaused.
*
* @param time The time parameter can either be a number or a parsable time string. If the
* time to jump to is greater than the total duration of the action, it will throw an IllegalOperationError.
* @param ignoreDelay If set to true, the delay will be ignored and the action will jump to
* the specified time in relation to the duration.
*
* @throws flash.errors.IllegalOperationError If the time to jump to is longer than the total time for the action.
*/
public function jumpToTime(time:*, ignoreDelay:Boolean = false):void {
// jumpToTime will fail if the action isn't running.
if (!isRunning) {
throw new IllegalOperationError("Can't jump to time if the action isn't running.");
return;
}
// parse time strings if this is a string.
var jumpTimeNumber:int;
//if time is a number
if (!isNaN(time)) {
jumpTimeNumber = int(time);
} else {
var timeString:String = time.toString();
jumpTimeNumber = timeStringParser.parseTimeString(timeString);
}
// Convert the jump time into a timestamp
var jumpTime:Timestamp =TimestampUtil.getTimestampFromMilliseconds(jumpTimeNumber);
// Ignore the delay in this equation if ignoreDelay is true.
var totalDuration:int = ignoreDelay ? duration : duration + delay;
// extract the jump time based on the action's timeUnit
var offsetTimestamp:Timestamp;
offsetTimestamp = TimestampUtil.getTimestampFromMilliseconds(delay);
// check that the jump time is valid
jumpTimeNumber = jumpTime.currentTime;
if (jumpTimeNumber > totalDuration) {
// you can't jump to a time that is past the end of the action's total time.
throw new IllegalOperationError("'time' must be less than the total time of the action.");
} else {
// If the action is paused, factor that into your jump (resluts wont appear until it's restarted)
var runningTime:Timestamp
if (isPaused) {
runningTime = TimestampUtil.subtract(_pauseTime, _startTime);
} else {
runningTime = TimestampUtil.subtract(Synchronizer.getInstance().currentTimestamp, _startTime);
}
// adjust the startTime to make it appear that the playhead should be at
// a different point in time on the next update.
_startTime = TimestampUtil.subtract(_startTime, TimestampUtil.subtract(jumpTime, runningTime));
// if ignoring the delay, also move the playhead forward by the delay amount.
if (ignoreDelay) {
_startTime = TimestampUtil.subtract(_startTime, offsetTimestamp);
}
}
}
// TODO add jumpByTime() method
/**
* Flips the values for to and from values. Essentially, causes the animation to run backwards.
*
* @see #cloneReversed()
*/
public function reverse():void {
for each (var target:ITweenTarget in _tweenTargets) {
var temp:Number = target.startValue;
target.startValue = target.endValue;
target.endValue = temp;
}
}
override public function clone():IAction {
var clonedTargets:Array = new Array();
for each (var target:ITweenTarget in _tweenTargets) {
var tweenTargetClone:ITweenTarget = target.clone();
clonedTargets.push(tweenTargetClone);
}
var clone:lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween = newWithTweenTarget(clonedTargets, _duration, _delay, _easingFunction);
clone._easingMod1 = _easingMod1;
clone._easingMod2 = _easingMod2;
clone.autoDelete = _autoDelete;
clone.snapToValueOnComplete = _snapToValueOnComplete;
return clone;
}
/**
* Creates a copy of this Tween which targets a different object and / or property.
* This is mostly used as a convenient way to reuse a tween, e.g. in a sequence.
* NOTE: If there are multiple target properties, this will only copy the first one in the array.
*
* @use
* var tween:Tween = new Tween(foo, "x", 100, 200);
* var sequence:Sequence = new Sequence(
* tween, // tweens foo's x property from 100 to 200
* tween.cloneWithTarget(foo, y) // tweens foo's y property from 100 to 200
* tween.cloneWithTarget(bar, y) // tweens bar's y property from 100 to 200
* );
*
*
* @see #clone()
*
* @deprecated - use multiple targetProperties instead.
* @see #addTweenTarget()
*
* @param target - The new object to target. Defaults to the same target as this.
* @param property - The new target object's property to target.
* @return Tween - a copy of this tween with a new target/property.
*/
public function cloneWithTarget(target:Object, property:String):lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween {
// get the first target from the list.
var tweenTarget:ITweenTarget = ITweenTarget(_tweenTargets[0]);
// create a target property with the new target and property.
var newTargetProperty:TargetProperty = new TargetProperty(target, property, tweenTarget.startValue, tweenTarget.endValue);
var clone:lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween = lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween(this.cloneWithTweenTarget(newTargetProperty));
return clone;
}
/**
* Creates a new Tween and reverses the start and end values of the target property.
*
* @use
* var tween:Tween = new Tween(foo, "x", 100, 200);
* var sequence:Sequence = new Sequence(
* tween, // tweens foo's x from 100 to 200
* tween.cloneReversed() // tweens foo's x from 200 to 100
* tween.cloneReversed(bar) // tweens bar's x from 200 to 100
* tween.cloneReversed(foo, y) // tweens foo's y from 200 to 100
* );
*
*
* @see #cloneWithTarget()
* @see #reverse()
*
* @param target - The optional target object of the new Tween
* @param property - The optional property to tween with the new Tween.
* @returns Tween - A new Tween identical to this but with start and end reversed.
*/
public function cloneReversed(target:Object = null, property:String = null):lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween {
var clone:lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween;
if (target != null && property != null) {
clone = lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween(cloneWithTarget(target, property));
} else {
clone = lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween(this.clone());
}
clone.reverse();
return clone;
}
/**
* Clones the tween with a new tweenTarget.
*
* @deprecated - Use multiple target properties insread.
* @see #addTweenTarget()
*/
public function cloneWithTweenTarget(tweenTarget:*):lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween {
var clone:lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween = lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween(this.clone());
clone.removeAllTweenTargets();
if (tweenTarget is ITweenTarget) {
clone.addTweenTarget(tweenTarget);
} else if (tweenTarget is Array) {
for each (var target:ITweenTarget in tweenTarget) {
clone.addTweenTarget(target);
}
} else {
throw new TypeError("'tweenTarget' param must be of type Array or of type ITweenTarget");
}
return clone;
}
/**
* Clean up references to target
*/
override public function kill():void {
super.kill();
removeAllTweenTargets();
}
/**
* Returns either the _id or a description of the tween.
*/
override public function toString():String {
var string:String = "lib_flex_animation_code_10_org_as3lib_kitchensync_action_KSTween[";
for each (var target:ITweenTarget in _tweenTargets) {
string += Object(target).toString() + ", ";
}
string += "]";
return string;
}
}
}