package flare.animate { import flare.util.IValueProxy; import flare.util.Property; import flash.display.DisplayObject; import flash.geom.Rectangle; import flash.utils.Dictionary; /** * Parallel transition with convenience methods for adding new * object tweens, helping to incrementally construct a group of transitions. * A Transitioner will automatically generate tweens for any number of * items and properties, simplifying the task of creating animated * transitions. * *
For example, the following code creates a 1 second * animation for two items. The first item is translated to the point * (50,50) and the second item is scaled along the x dimension to twice the * normal size.
** var item1:Sprite, item2:Sprite; // assume these are two drawn sprites * var t:Transitioner = new Transitioner(1); // create 1-second transition * t.$(item1).x = 50; * t.$(item1).y = 50; * t.$(item2).scaleX = 2; * t.play(); ** *
In the code above, the $
method takes an item (this
* can be any ActionScript object, but is often a DisplayObject
* instance) and returns an Object
which stores the names of
* the properties to animate and their target values. Behind the scenes,
* the Transitioner
automatically creates Tween
* objects as needed.
The object returned by the $
method is a proxy object
* that passes the values to underlying tweens as needed. This same proxy
* object is reused across calls to the $
method so do
* not attempt to use multiple return values from the
* $
method simultaneously. The following example shows
* what you should not do!
* var o1:Object = t.$(item1); * var o2:Object = t.$(item2); // o2==o1, now configured for item2 * o1.x = 5; // actually sets the value 5 to item2, NOT item1 ** *
* A transitioner can also be set to "immediate" mode, either by setting
* the immediate
property to true, or by passing in
* NaN
as the duration value to the constructor. When in
* immediate mode, a transitioner will NOT generate
* Tween
instances to animate the properties. Instead, the
* transitioner will set the values of the target objects immediately.
* For example, when in immediate mode, the $
operator is
* equivalent to directly setting the property:
* t.$(item1).x = 50
has exactly the same result at
* t.x = 50
. The static property
* Transitioner.DEFAULT
provides a default instance of an
* immediate-mode transitioner.
*
* With these features, transitioners provide a highly flexible way to * update values in your application. You can write layout and other * methods once, using a transitioner to update all the property values. * When animation is desired, a standard transitioner can be passed in * to your routines. When immediate updates are desired, you can reuse * the same code, but just pass in a transitioner in immediate mode * instead. Whether or not value updates are animated or immediate then * becomes easy to control. *
* *
* Transitioners also provide optimizations to improve animation
* performance. However, they are not enabled by default, as the
* optimizations make some assumptions about how the transitioner will
* be used. See the optimize
property and
* dispose
method for more information.
*
Transitioner.DEFAULT
is returned.
* @return a Transitioner instance determined by the input
*/
public static function instance(t:*):Transitioner {
if (t is Number) {
var dur:Number = Number(t);
return dur<0 ? Transitioner.DEFAULT : new Transitioner(dur);
} else if (t == null) {
return Transitioner.DEFAULT;
} else {
return t as Transitioner;
}
}
// --------------------------------------------------------------------
private var _immediate:Boolean;
private var _lookup:/*Object->Tween*/Dictionary = new Dictionary();
private var _proxy:ValueProxy;
private var _optimize:Boolean = false;
private var _subdur:Number;
/** @private */
public override function get duration():Number {
return _trans.length==0 ? _subdur : super.duration;
}
/** Immediate mode flag, used to bypass tween generation and perform
* immediate updates of target object values. */
public function get immediate():Boolean { return _immediate; }
public function set immediate(b:Boolean):void {
_immediate = b;
if (!immediate && _proxy == null) _proxy = new ValueProxy(this);
}
/**
* Flag indicating if aggressive optimization should be applied.
* This can significantly decrease processing time when large numbers
* of elements are involved. However, the optimization process makes a
* few assumptions about how the transitioner will be used. If these
* assumptions are not met, the animations may exhibit unexpected
* behaviors.
*
* The assumptions made for optimized transitioners are: *
Sequence
.Tween
and Interpolator
* instances, reducing initialization time across transitioners by
* reusing objects.optimize
property for mode details.
* @param id an optional id. If non-null, any other running transition
* with the same id will be canceled when this transitioner is played.
*/
public function Transitioner(duration:Number=1, easing:Function=null,
optimize:Boolean=false, id:String=null)
{
super.easing = easing==null ? DEFAULT_EASING : easing;
_subdur = duration;
_optimize = optimize;
_immediate = isNaN(duration);
if (id!=null) this.id = id;
if (!_immediate) _proxy = new ValueProxy(this);
}
/**
* Indicates if the Transitioner contains a Tween for the given object.
* @param o the object to test for
* @return true if there is a Tween for the object, false otherwise
*/
public function hasTweenFor(o:Object):Boolean
{
return _immediate ? false : (_lookup[o] != undefined);
}
/**
* Returns the Tween for the given object, creating a new tween if no
* tween is yet associated with the object. This method returns null if
* the transitioner is in immediate mode.
* @param o the target object
* @return a tween for the input target object, or null if this
* transitioner is in immediate mode.
*/
public function _(o:Object):Tween
{
if (_immediate) return null;
var tw:Tween = _lookup[o];
if (tw == null) {
add(tw = getTween(o, _subdur));
tw.easing = Easing.none;
_lookup[o] = tw;
}
return tw;
}
/**
* Returns the values object of the Tween for the given object. If no
* tween is associated with the object, a new one is created.
*
* If the transitioner is in immediate mode, then no Tween will be
* created. Instead, the input object will be returned. This allows
* value updates to be set immediately, rather than animated by a
* tween.
* @param o the target object
* @return the values
object for the target object's
* tween, or the target object itself if this transitioner is in
* immediate mode.
*/
public function $(o:Object):Object
{
return _immediate ? o : _proxy.init(o);
}
/**
* Sets property values for a target object. This method has the same
* effect as setting a property on the object returned by the
* $
method.
*
* If the transitioner is in immediate mode, the property name will * be parsed and the value set at the end of the property chain. If * the transitioner is not in immediate mode, the property name and * values will simply be added to a Tween. If no Tween is associated * with the input object, a new one will be created.
* * @param o the target object * @param name the property name string * @param value the property value to set */ public function setValue(o:Object, name:String, value:*):void { if (_immediate) { // set the object property Property.$(name).setValue(o, value); } else if (optimize && getValue(o, name) == value) { // do nothing, optimize the call away... } else if (optimize && o is DisplayObject && !getValue(o, "visible")) { Property.$(name).setValue(o, value); } else { // add to a tween _(o).values[name] = value; } } /** * Retrieves property values for a target object. This method has the * same effect as accessing a property using the object returned by the *$
method.
*
* If the transitioner is in immediate mode, the property name will
* be parsed and the value retrieved diretly from the target object. If
* the transitioner is not in immediate mode, this method will first
* try to lookup the value in the tween values
for the
* target object. If this does not succeed, the property value will be
* retrieved directly from the target object itself as in the immediate
* mode case.
optimize
flag is true.
* Otherwise, this method can be invoked manually when a transitioner
* is no longer needed.
*/
public override function dispose():void
{
while (_trans.length > 0) {
var t:Transition = _trans.pop();
t.dispose();
if (t is Tween) reclaimTween(t as Tween);
}
}
/**
* Computes the approximate size of the given object after this
* transitioner has been run. This calculation is performed by
* applying the final scaleX
, scaleY
, and
* size
values of the object.
* @param d the display object to compute the size for
* @param r a rectangle for storing the results
* @return a rectangle whose width
and height
* properties contain the end size values.
*/
public function endSize(d:DisplayObject, r:Rectangle=null):Rectangle
{
if (r==null) r = new Rectangle();
var t:Tween, v:Object, o:Object = Object(d);
var scaleX:Number, scaleY:Number, size:Number;
if (_immediate || (t=_lookup[d])==null) {
r.width = d.width;
r.height = d.height;
} else {
v = t.values;
if (v.hasOwnProperty("scaleX")) {
scaleX = d.scaleX;
d.scaleX = v.scaleX;
}
if (v.hasOwnProperty("scaleY")) {
scaleY = d.scaleY;
d.scaleY = v.scaleY;
}
if (v.hasOwnProperty("size")) {
size = o.size;
o.size = v.size;
}
r.width = d.width;
r.height = d.height;
if (v.hasOwnProperty("scaleX")) d.scaleX = scaleX;
if (v.hasOwnProperty("scaleY")) d.scaleY = scaleY;
if (v.hasOwnProperty("size")) o.size = size;
}
return r;
}
/**
* Computes the approximate bounds of the given object after this
* transitioner has been run. This calculation is performed by
* applying the final scaleX
, scaleY
,
* size
, x
, and y
values of
* the object.
* @param d the display object to compute the size for
* @param coords the target coordinate space for the bounds
* @return a rectangle whose width
and height
* properties contain the end size values.
*/
public function endBounds(d:DisplayObject,
coords:DisplayObject):Rectangle
{
var r:Rectangle = new Rectangle();
var t:Tween, v:Object, o:Object = Object(d);
var scaleX:Number, scaleY:Number, size:Number, x:Number, y:Number;
if (_immediate || (t=_lookup[d])==null) {
r = d.getBounds(coords);
} else {
v = t.values;
if (v.hasOwnProperty("scaleX")) {
scaleX = d.scaleX;
d.scaleX = v.scaleX;
}
if (v.hasOwnProperty("scaleY")) {
scaleY = d.scaleY;
d.scaleY = v.scaleY;
}
if (v.hasOwnProperty("size")) {
size = o.size;
o.size = v.size;
}
if (v.hasOwnProperty("x")) {
x = d.x;
d.x = v.x;
}
if (v.hasOwnProperty("y")) {
y = d.y;
d.y = v.y;
}
r = d.getBounds(coords);
if (v.hasOwnProperty("scaleX")) d.scaleX = scaleX;
if (v.hasOwnProperty("scaleY")) d.scaleY = scaleY;
if (v.hasOwnProperty("size")) o.size = size;
if (v.hasOwnProperty("x")) d.x = x;
if (v.hasOwnProperty("y")) d.y = y;
}
return r;
}
// --------------------------------------------------------------------
private static var _maxPoolSize:int = 10000;
private static var _tweenPool:Vector.