/** * VERSION: 1.06 * DATE: 2011-01-12 * AS2 (AS3 is also available) * UPDATES AND DOCS AT: http://www.greensock.com **/ /** * TweenNano is a super-lightweight (1.6k in AS3 and 2k in AS2) version of TweenLite * and is only recommended for situations where you absolutely cannot afford the extra 3.3k (5.3k total) that the normal * TweenLite engine would cost and your project doesn't require any plugins. Otherwise, I'd strongly advocate using * TweenLite because of the additional flexibility it provides via plugins and compatibility with TimelineLite and TimelineMax. * TweenNano can do everything TweenLite can do with the following exceptions: * * *
* SPECIAL PROPERTIES: *

* * Any of the following special properties can optionally be passed in through the vars object (the third parameter): * * * * EXAMPLES:

* * Tween the the MovieClip "mc" to an _alpha value of 50 and an x-coordinate of 120 * over the course of 1.5 seconds like so:

* * * import com.greensock.TweenNano;

* TweenNano.to(mc, 1.5, {_alpha:50, _x:120}); *


* * To tween the "mc" MovieClip's _alpha property to 50, its _x property to 120 using the Back.easeOut easing * function, delay starting the whole tween by 2 seconds, and then call a function named "onFinishTween" when it * has completed (it will have a duration of 5 seconds) and pass a few parameters to that function (a value of * 5 and a reference to the mc), you'd do so like:

* * * import com.greensock.TweenNano;
* import com.greensock.easing.Back;

* * TweenNano.to(mc, 5, {_alpha:50, _x:120, ease:Back.easeOut, delay:2, onComplete:onFinishTween, onCompleteParams:[5, mc]});
* function onFinishTween(param1:Number, param2:MovieClip):Void {
* trace("The tween has finished! param1 = " + param1 + ", and param2 = " + param2);
* } *


* * If you have a MovieClip on the stage that is already in it's end position and you just want to animate it into * place over 5 seconds (drop it into place by changing its _y property to 100 pixels higher on the screen and * dropping it from there), you could:

* * * import com.greensock.TweenNano;
* import com.greensock.easing.Elastic;

* * TweenNano.from(mc, 5, {_y:"-100", ease:Elastic.easeOut}); *


* * NOTES / TIPS:

* * * Copyright 2011, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for corporate Club GreenSock members, the software agreement that was issued with the corporate membership. * * @author Jack Doyle, jack@greensock.com */ class com.greensock.TweenNano { /** @private **/ private static var version:Number = 1.06; /** @private **/ private static var _time:Number; /** @private **/ private static var _frame:Number; /** @private Holds references to all our tweens based on their targets (an Array for each target) **/ private static var _masterList:Object = {}; /** @private A reference to the Shape that we use to drive all our ENTER_FRAME events. **/ private static var _timingClip:MovieClip; /** @private Indicates whether or not the TweenNano class has been initted. **/ private static var _tnInitted:Boolean; /** @private **/ private static var _reservedProps:Object = {ease:1, delay:1, useFrames:1, overwrite:1, onComplete:1, onCompleteParams:1, runBackwards:1, immediateRender:1, onUpdate:1, onUpdateParams:1}; /** @private **/ private static var _cnt:Number = -16000; /** Duration of the tween in seconds (or in frames if "useFrames" is true). **/ public var duration:Number; /** Stores variables (things like "alpha", "y" or whatever we're tweening, as well as special properties like "onComplete"). **/ public var vars:Object; /** @private Start time in seconds (or frames for frames-based tweens) **/ public var startTime:Number; /** Target object whose properties this tween affects. This can be ANY object, not just a MovieClip. **/ public var target:Object; /** @private Indicates whether or not the tween is currently active **/ public var active:Boolean; /** @private Flagged for garbage collection **/ public var gc:Boolean; /** Indicates that frames should be used instead of seconds for timing purposes. So if useFrames is true and the tween's duration is 10, it would mean that the tween should take 10 frames to complete, not 10 seconds. **/ public var useFrames:Boolean; /** @private Target ID (a way to identify each end target, i.e. "t1", "t2", "t3") **/ public var endTargetID:String; /** @private result of _ease(this.time, 0, 1, this.duration). Usually between 0 and 1, but not always (like with Elastic.easeOut). **/ public var ratio:Number; /** @private Easing method to use which determines how the values animate over time. Examples are Elastic.easeOut and Strong.easeIn. Many are found in the fl.motion.easing package or com.greensock.easing. **/ private var _ease:Function; /** @private Indicates whether or not init() has been called (where all the tween property start/end value information is recorded) **/ private var _initted:Boolean; /** @private Contains parsed data for each property that's being tweened (property name, start, and change) **/ private var _propTweens:Array; /** * Constructor * * @param target Target object whose properties this tween affects. This can be ANY object, not just a MovieClip. * @param duration Duration in seconds (or in frames if "useFrames" is true) * @param vars An object containing the end values of the properties you're tweening, like {_x:100, _y:50}. It can also contain special properties like "onComplete", "ease", "delay", etc. */ public function TweenNano(target:Object, duration:Number, vars:Object) { if (!_tnInitted) { _time = getTimer() * 0.001; _frame = 0; _tnInitted = true; } if (_timingClip.onEnterFrame != updateAll) { //subloaded swfs in Flash Lite restrict access to _root.createEmptyMovieClip(), so we find the subloaded swf MovieClip to createEmptyMovieClip(), but if it gets unloaded, the onEnterFrame will stop running so we need to check each time a tween is created. var mc:MovieClip = (_root.getBytesLoaded() == undefined) ? findSubloadedSWF(_root) : _root; //subloaded swfs won't return getBytesLoaded() in Flash Lite, and it locks us out from being able to createEmptyMovieClip(), so we must find the subloaded clip to do it there instead. var l:Number = 999; //Don't just do getNextHighestDepth() because often developers will hard-code stuff that uses low levels which would overwrite the TweenLite clip. Start at level 999 and make sure nothing's there. If there is, move up until we find an empty level. while (mc.getInstanceAtDepth(l) != undefined) { l++; } _timingClip = mc.createEmptyMovieClip("__tweenNano" + String(version).split(".").join("_"), l); _timingClip.onEnterFrame = updateAll; } this.vars = vars; this.duration = duration; this.ratio = 0; this.active = Boolean(duration == 0 && this.vars.delay == 0 && this.vars.immediateRender != false); this.target = target; if (typeof(this.vars.ease) != "function") { _ease = TweenNano.easeOut; } else { _ease = this.vars.ease; } _propTweens = []; this.useFrames = Boolean(vars.useFrames == true); var delay:Number = this.vars.delay || 0; this.startTime = (this.useFrames) ? _frame + delay : _time + delay; this.endTargetID = getID(target, true); var a:Array = _masterList[this.endTargetID].tweens; if (a == undefined || Number(this.vars.overwrite) == 1 || this.vars.overwrite == undefined) { _masterList[this.endTargetID] = {target:target, tweens:[this]}; } else { a[a.length] = this; } if (this.active || this.vars.immediateRender) { renderTime(0); } } /** * @private * Initializes the property tweens, determining their start values and amount of change. * Also triggers overwriting if necessary and sets the _hasUpdate variable. */ public function init():Void { for (var p:String in this.vars) { if (!_reservedProps[p]) { _propTweens[_propTweens.length] = [p, this.target[p], (typeof(this.vars[p]) == "number") ? this.vars[p] - this.target[p] : Number(this.vars[p])]; //[property, start, change] } } if (this.vars.runBackwards) { var pt:Array; var i:Number = _propTweens.length; while (--i > -1) { pt = _propTweens[i]; pt[1] += pt[2]; pt[2] = -pt[2]; } } _initted = true; } /** * Renders the tween at a particular time (or frame number for frames-based tweens) * WITHOUT changing its startTime, meaning if the tween is in progress when you call * renderTime(), it will not adjust the tween's timing to continue from the new time. * The time is based simply on the overall duration. For example, if a tween's duration * is 3, renderTime(1.5) would render it at the halfway finished point. * * @param time time (or frame number for frames-based tweens) to render. */ public function renderTime(time:Number):Void { if (!_initted) { init(); } var pt:Array, i:Number = _propTweens.length; if (time >= this.duration) { time = this.duration; this.ratio = 1; } else if (time <= 0) { this.ratio = 0; } else { this.ratio = _ease(time, 0, 1, this.duration); } while (--i > -1) { pt = _propTweens[i]; this.target[pt[0]] = pt[1] + (this.ratio * pt[2]); } if (this.vars.onUpdate) { this.vars.onUpdate.apply(this.vars.onUpdateScope, this.vars.onUpdateParams); } if (time == this.duration) { complete(true); } } /** * Forces the tween to completion. * * @param skipRender To skip rendering the final state of the tween, set skipRender to true. */ public function complete(skipRender:Boolean):Void { if (skipRender != true) { renderTime(this.duration); return; } kill(); if (this.vars.onComplete) { this.vars.onComplete.apply(this.vars.onCompleteScope, this.vars.onCompleteParams); } } /** Kills the tween, stopping it immediately. **/ public function kill():Void { this.gc = true; this.active = false; } //---- STATIC FUNCTIONS ------------------------------------------------------------------------- /** * Static method for creating a TweenNano instance which can be more intuitive for some developers * and shields them from potential garbage collection issues that could arise when assigning a * tween instance to a variable that persists. The following lines of code all produce exactly * the same result:

* * var myTween:TweenNano = new TweenNano(mc, 1, {_x:100});
* TweenNano.to(mc, 1, {_x:100});
* var myTween:TweenNano = TweenNano.to(mc, 1, {_x:100});
* * @param target Target object whose properties this tween affects. This can be ANY object, not just a MovieClip. * @param duration Duration in seconds (or frames if "useFrames" is true) * @param vars An object containing the end values of the properties you're tweening, like {_x:100, _y:50}. It can also contain special properties like "onComplete", "ease", "delay", etc. * @return TweenNano instance */ public static function to(target:Object, duration:Number, vars:Object):TweenNano { return new TweenNano(target, duration, vars); } /** * Static method for creating a TweenNano instance that tweens in the opposite direction * compared to a TweenNano.to() tween. In other words, you define the START values in the * vars object instead of the end values, and the tween will use the current values as * the end values. This can be very useful for animating things into place on the stage * because you can build them in their end positions and do some simple TweenNano.from() * calls to animate them into place. NOTE: By default, immediateRender * is true in from() tweens, meaning that they immediately render their starting state * regardless of any delay that is specified. You can override this behavior by passing * immediateRender:false in the vars object so that it will wait to * render until the tween actually begins. To illustrate the default behavior, the following code * will immediately set the alpha of mc to 0 and then wait 2 seconds * before tweening the alpha back to 1 over the course of 1.5 seconds:

* * TweenNano.from(mc, 1.5, {_alpha:0, delay:2}); * * @param target Target object whose properties this tween affects. This can be ANY object, not just a MovieClip. * @param duration Duration in seconds (or frames if "useFrames" is true) * @param vars An object containing the start values of the properties you're tweening like {x:100, y:50}. It can also contain special properties like "onComplete", "ease", "delay", etc. * @return TweenNano instance */ public static function from(target:Object, duration:Number, vars:Object):TweenNano { vars.runBackwards = true; if (vars.immediateRender == undefined) { vars.immediateRender = true; } return new TweenNano(target, duration, vars); } /** * Provides a simple way to call a function after a set amount of time (or frames). You can * optionally pass any number of parameters to the function too. For example:

* * TweenNano.delayedCall(1, myFunction, ["param1", 2]);
* function myFunction(param1:String, param2:Number):Void {
* trace("called myFunction and passed params: " + param1 + ", " + param2);
* }
* * @param delay Delay in seconds (or frames if "useFrames" is true) before the function should be called * @param onComplete Function to call * @param onCompleteParams An Array of parameters to pass the function. * @param onCompleteScope the scope that should be used for the delayed function call (scope basically defines what "this" refers to in the function). * @param useFrames If true, the delay will be measured in frames instead of seconds. * @return TweenNano instance */ public static function delayedCall(delay:Number, onComplete:Function, onCompleteParams:Array, onCompleteScope:Object, useFrames:Boolean):TweenNano { return new TweenNano(onComplete, 0, {delay:delay, onComplete:onComplete, onCompleteParams:onCompleteParams, onCompleteScope:onCompleteScope, useFrames:useFrames, overwrite:0}); } /** * @private * Updates active tweens and activates those whose startTime is before the _time/_frame. */ public static function updateAll():Void { _frame++; _time = getTimer() * 0.001; var ml:Object = _masterList, a:Array, tgt:String, i:Number, t:Number, tween:TweenNano; for (tgt in ml) { a = ml[tgt].tweens; i = a.length; while (--i > -1) { tween = a[i]; t = (tween.useFrames) ? _frame : _time; if (tween.active || (!tween.gc && t >= tween.startTime)) { tween.renderTime(t - tween.startTime); } else if (tween.gc) { a.splice(i, 1); } } if (a.length == 0) { delete ml[tgt]; } } } /** * Kills all the tweens of a particular object, optionally forcing them to completion too. * * @param target Object whose tweens should be immediately killed * @param complete Indicates whether or not the tweens should be forced to completion before being killed. */ public static function killTweensOf(target:Object, complete:Boolean):Void { var id:String = getID(target, true); var a:Array = _masterList[id], i:Number; if (a) { if (complete) { i = a.length; while (--i > -1) { if (!a[i].gc) { a[i].complete(false); } } } delete _masterList[id]; } } /** @private **/ public static function getID(target:Object, lookup:Boolean):String { var id:String, ml:Object = _masterList; if (lookup) { if (typeof(target) == "movieclip") { if (ml[String(target)] != undefined) { return String(target); } else { id = String(target); ml[id] = {target:target, tweens:[]}; return id; } } else { for (var p:String in ml) { if (ml[p].target == target) { return p; } } } } _cnt++; id = "t" + _cnt; ml[id] = {target:target, tweens:[]}; return id; } /** @private **/ private static function easeOut(t:Number, b:Number, c:Number, d:Number):Number { return -1 * (t /= d) * (t - 2); } /** * @private * Subloaded SWFs in Flash Lite may not have access to _root.createEmptyMovieClip(), so we needed a way * to step through all the MovieClips on the Stage (including nested ones) until one is found that * can be used with createEmptyMovieClip(). Note: when getBytesLoaded() returns undefined, we know that * the MovieClip has been deemed off-limits by the Flash Player and it won't allow createEmptyMovieClip() * on that MovieClip. **/ private static function findSubloadedSWF(mc:MovieClip):MovieClip { for (var p:String in mc) { if (typeof(mc[p]) == "movieclip") { if (mc[p]._url != _root._url && mc[p].getBytesLoaded() != undefined) { return mc[p]; } else if (findSubloadedSWF(mc[p]) != undefined) { return findSubloadedSWF(mc[p]); } } } return undefined; } }