TweenCore.as 21.6 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
/**
 * VERSION: 1.382
 * DATE: 2010-05-25
 * ACTIONSCRIPT VERSION: 3.0 (AS2 version is also available)
 * UPDATES AND DOCUMENTATION AT: http://www.TweenLite.com
 **/

package com.greensock.core {
	import com.greensock.*;
/**
 * TweenCore is the base class for all TweenLite, TweenMax, TimelineLite, and TimelineMax classes and 
 * provides core functionality and properties. There is no reason to use this class directly.<br /><br />
 * 
 * <b>Copyright 2010, GreenSock. All rights reserved.</b> This work is subject to the terms in <a href="http://www.greensock.com/terms_of_use.html">http://www.greensock.com/terms_of_use.html</a> or for corporate Club GreenSock members, the software agreement that was issued with the corporate membership.
 * 
 * @author Jack Doyle, jack@greensock.com
 */
	public class TweenCore {
		/** @private **/
		public static const version:Number = 1.382;
		
		/** @private **/
		protected static var _classInitted:Boolean;
		
		/** @private Delay in seconds (or frames for frames-based tweens/timelines) **/
		protected var _delay:Number; 
		/** @private Has onUpdate. Tracking this as a Boolean value is faster than checking this.vars.onUpdate != null. **/
		protected var _hasUpdate:Boolean;
		/** @private Primarily used for zero-duration tweens to determine the direction/momentum of time which controls whether the starting or ending values should be rendered. For example, if a zero-duration tween renders and then its timeline reverses and goes back before the startTime, the zero-duration tween must render the starting values. Otherwise, if the render time is zero or later, it should always render the ending values. **/
		protected var _rawPrevTime:Number = -1;
		/** @private **/
		protected var _pauseTime:Number;
		
		/** Stores variables (things like alpha, y or whatever we're tweening as well as special properties like "onComplete"). **/
		public var vars:Object; 
		/** @private The tween has begun and is now active **/
		public var active:Boolean; 
		/** @private Flagged for garbage collection **/
		public var gc:Boolean; 
		/** @private Indicates whether or not init() has been called (where all the tween property start/end value information is recorded) **/
		public var initted:Boolean; 
		 /** The parent timeline on which the tween/timeline is placed. By default, it uses the TweenLite.rootTimeline (or TweenLite.rootFramesTimeline for frames-based tweens/timelines). **/
		public var timeline:SimpleTimeline;
		/** @private Start time in seconds (or frames for frames-based tweens/timelines), according to its position on its parent timeline **/
		public var cachedStartTime:Number; 
		/** @private The last rendered currentTime of this TweenCore. If a tween is going to repeat, its cachedTime will reset even though the cachedTotalTime continues linearly (or if it yoyos, the cachedTime may go forwards and backwards several times over the course of the tween). The cachedTime reflects the tween's "local" (which can never exceed the duration) time whereas the cachedTotalTime reflects the overall time. These will always match if the tween doesn't repeat/yoyo.**/
		public var cachedTime:Number; 
		/** @private The last rendered totalTime of this TweenCore. It is prefaced with "cached" because using a public property like this is faster than using the getter which is essentially a function call. If you want to update the value, you should always use the normal property, like myTween.totalTime = 0.5.**/
		public var cachedTotalTime:Number; 
		/** @private Prefaced with "cached" because using a public property like this is faster than using the getter which is essentially a function call. If you want to update the value, you should always use the normal property, like myTween.duration = 0.5.**/
		public var cachedDuration:Number; 
		/** @private Prefaced with "cached" because using a public property like this is faster than using the getter which is essentially a function call. If you want to update the value, you should always use the normal property, like myTween.totalDuration = 0.5.**/
		public var cachedTotalDuration:Number; 
		/** @private timeScale allows you to slow down or speed up a tween/timeline. 1 = normal speed, 0.5 = half speed, 2 = double speed, etc. It is prefaced with "cached" because using a public property like this is faster than using the getter which is essentially a function call. If you want to update the value, you should always use the normal property, like myTween.timeScale = 2**/
		public var cachedTimeScale:Number;
		/** @private Indicates whether or not the tween is reversed. **/ 
		public var cachedReversed:Boolean;
		/** @private Next TweenCore object in the linked list.**/
		public var nextNode:TweenCore; 
		/** @private Previous TweenCore object in the linked list**/
		public var prevNode:TweenCore; 
		/** @private When a TweenCore has been removed from its timeline, it is considered an orphan. When it it added to a timeline, it is no longer an orphan. We don't just set its "timeline" property to null because we need to always keep track of the timeline in case the TweenCore is enabled again by restart() or basically any operation that would cause it to become active again. "cachedGC" is different in that a TweenCore could be eligible for gc yet not removed from its timeline, like when a TimelineLite completes for example. **/
		public var cachedOrphan:Boolean;
		/** @private Indicates that the duration or totalDuration may need refreshing (like if a TimelineLite's child had a change in duration or startTime). This is another performance booster because if the cache isn't dirty, we can quickly read from the cachedDuration and/or cachedTotalDuration **/
		public var cacheIsDirty:Boolean; 
		/** @private Quicker way to read the paused property. It is public for speed purposes. When setting the paused state, always use the regular "paused" property.**/
		public var cachedPaused:Boolean; 
		/** Place to store any data you want.**/
		public var data:*; 
		
		public function TweenCore(duration:Number=0, vars:Object=null) {
			this.vars = (vars != null) ? vars : {};
			this.cachedDuration = this.cachedTotalDuration = duration;
			_delay = (this.vars.delay) ? Number(this.vars.delay) : 0;
			this.cachedTimeScale = (this.vars.timeScale) ? Number(this.vars.timeScale) : 1;
			this.active = Boolean(duration == 0 && _delay == 0 && this.vars.immediateRender != false);
			this.cachedTotalTime = this.cachedTime = 0;
			this.data = this.vars.data;
			
			if (!_classInitted) {
				if (isNaN(TweenLite.rootFrame)) {
					TweenLite.initClass();
					_classInitted = true;
				} else {
					return;
				}
			}
			
			var tl:SimpleTimeline = (this.vars.timeline is SimpleTimeline) ? this.vars.timeline : (this.vars.useFrames) ? TweenLite.rootFramesTimeline : TweenLite.rootTimeline;
			this.cachedStartTime = tl.cachedTotalTime + _delay;
			tl.addChild(this);
			if (this.vars.reversed) {
				this.cachedReversed = true;
			}
			if (this.vars.paused) {
				this.paused = true;
			}
		}
		
		/** Starts playing forward from the current position. (essentially unpauses and makes sure that it is not reversed) **/
		public function play():void {
			this.reversed = false;
			this.paused = false;
		}
		
		/** Pauses the tween/timeline **/
		public function pause():void {
			this.paused = true;
		}
		
		/** Starts playing from the current position without altering direction (forward or reversed). **/
		public function resume():void {
			this.paused = false;
		}
		
		/**
		 * Restarts and begins playing forward.
		 * 
		 * @param includeDelay Determines whether or not the delay (if any) is honored in the restart()
		 * @param suppressEvents If true, no events or callbacks will be triggered as the "virtual playhead" moves to the new position (onComplete, onUpdate, onReverseComplete, etc. of this tween/timeline and any of its child tweens/timelines won't be triggered, nor will any of the associated events be dispatched) 
		 */
		public function restart(includeDelay:Boolean=false, suppressEvents:Boolean=true):void {
			this.reversed = false;
			this.paused = false;
			this.setTotalTime((includeDelay) ? -_delay : 0, suppressEvents);
		}
		
		/**
		 * Reverses smoothly, adjusting the startTime to avoid any skipping. After being reversed,
		 * it will play backwards, exactly opposite from its forward orientation, meaning that, for example, a
		 * tween's easing equation will appear reversed as well. If a tween/timeline plays for 2 seconds and gets
		 * reversed, it will play for another 2 seconds to return to the beginning.
		 * 
		 * @param forceResume If true, it will resume() immediately upon reversing. Otherwise its paused state will remain unchanged.
		 */
		public function reverse(forceResume:Boolean=true):void {
			this.reversed = true;
			if (forceResume) {
				this.paused = false;
			} else if (this.gc) {
				this.setEnabled(true, false);
			}
		}
		
		/**
		 * @private
		 * Renders the tween/timeline at a particular time (or frame number for frames-based tweens)
		 * WITHOUT changing its startTime. For example, if a tween's duration
		 * is 3, <code>renderTime(1.5)</code> would render it at the halfway finished point.
		 * 
		 * @param time time in seconds (or frame number for frames-based tweens/timelines) to render.
		 * @param suppressEvents If true, no events or callbacks will be triggered for this render (like onComplete, onUpdate, onReverseComplete, etc.)
		 * @param force Normally the tween will skip rendering if the time matches the cachedTotalTime (to improve performance), but if force is true, it forces a render. This is primarily used internally for tweens with durations of zero in TimelineLite/Max instances.
		 */
		public function renderTime(time:Number, suppressEvents:Boolean=false, force:Boolean=false):void {
			
		}
		
		/**
		 * Forces the tween/timeline to completion.
		 * 
		 * @param skipRender to skip rendering the final state of the tween, set skipRender to true. 
		 * @param suppressEvents If true, no events or callbacks will be triggered for this render (like onComplete, onUpdate, onReverseComplete, etc.)
		 */
		public function complete(skipRender:Boolean=false, suppressEvents:Boolean=false):void {
			if (!skipRender) {
				renderTime(this.totalDuration, suppressEvents, false); //just to force the final render
				return; //renderTime() will call complete() again, so just return here.
			}
			if (this.timeline.autoRemoveChildren) {
				this.setEnabled(false, false);
			} else {
				this.active = false;
			}
			if (!suppressEvents) {
				if (this.vars.onComplete && this.cachedTotalTime == this.cachedTotalDuration && !this.cachedReversed) { //note: remember that tweens can have a duration of zero in which case their cachedTime and cachedDuration would always match.
					this.vars.onComplete.apply(null, this.vars.onCompleteParams);
				} else if (this.cachedReversed && this.cachedTotalTime == 0 && this.vars.onReverseComplete) {
					this.vars.onReverseComplete.apply(null, this.vars.onReverseCompleteParams);
				}
			}
		}
		
		/** 
		 * Clears any initialization data (like starting values in tweens) which can be useful if, for example, 
		 * you want to restart it without reverting to any previously recorded starting values. When you invalidate() 
		 * a tween/timeline, it will be re-initialized the next time it renders and its <code>vars</code> object will be re-parsed. 
		 * The timing of the tween/timeline (duration, startTime, delay) will NOT be affected. Another example would be if you
		 * have a <code>TweenMax(mc, 1, {x:100, y:100})</code> that ran when mc.x and mc.y were initially at 0, but now mc.x 
		 * and mc.y are 200 and you want them tween to 100 again, you could simply <code>invalidate()</code> the tween and 
		 * <code>restart()</code> it. Without invalidating first, restarting it would cause the values jump back to 0 immediately 
		 * (where they started when the tween originally began). When you invalidate a timeline, it automatically invalidates 
		 * all of its children.
		 **/
		public function invalidate():void {
			
		}
		
		/**
		 * @private
		 * If a tween/timeline is enabled, it is eligible to be rendered (unless it is paused). Setting enabled to
		 * false essentially removes it from its parent timeline and stops protecting it from garbage collection.
		 * 
		 * @param enabled Enabled state of the tween/timeline
		 * @param ignoreTimeline By default, the tween/timeline will remove itself from its parent timeline when it is disabled, and add itself when it is enabled, but this parameter allows you to override that behavior.
		 * @return Boolean value indicating whether or not important properties may have changed when the TweenCore was enabled/disabled. For example, when a motionBlur (plugin) is disabled, it swaps out a BitmapData for the target and may alter the alpha. We need to know this in order to determine whether or not a new tween that is overwriting this one should be re-initted() with the changed properties. 
		 **/
		public function setEnabled(enabled:Boolean, ignoreTimeline:Boolean=false):Boolean {
			this.gc = !enabled;
			if (enabled) {
				this.active = Boolean(!this.cachedPaused && this.cachedTotalTime > 0 && this.cachedTotalTime < this.cachedTotalDuration);
				if (!ignoreTimeline && this.cachedOrphan) {
					this.timeline.addChild(this);
				}
			} else {
				this.active = false;
				if (!ignoreTimeline && !this.cachedOrphan) {
					this.timeline.remove(this, true);
				}
			}
			return false;
		}
		
		/** Kills the tween/timeline, stopping it immediately. **/
		public function kill():void {
			setEnabled(false, false);
		}
		
		/**
		 * @private
		 * Sets the cacheIsDirty property of all anscestor timelines (and optionally this tween/timeline too). Setting
		 * the cacheIsDirty property to true forces any necessary recalculation of its cachedDuration and cachedTotalDuration 
		 * properties and sorts the affected timelines' children TweenCores so that they're in the proper order 
		 * next time the duration or totalDuration is requested. We don't just recalculate them immediately because 
		 * it can be much faster to do it this way.
		 * 
		 * @param includeSelf indicates whether or not this tween's cacheIsDirty property should be affected.
		 */
		protected function setDirtyCache(includeSelf:Boolean=true):void {
			var tween:TweenCore = (includeSelf) ? this : this.timeline;
			while (tween) {
				tween.cacheIsDirty = true;
				tween = tween.timeline;
			}
		}
		
		/**
		 * @private
		 * Sort of like placing the local "playhead" at a particular totalTime and then aligning it with
		 * the parent timeline's "playhead" so that rendering continues from that point smoothly. This 
		 * changes the cachedStartTime.
		 * 
		 * @param time Time that should be rendered (includes any repeats and repeatDelays for TimelineMax)
		 * @param suppressEvents If true, no events or callbacks will be triggered for this render (like onComplete, onUpdate, onReverseComplete, etc.)
		 **/
		protected function setTotalTime(time:Number, suppressEvents:Boolean=false):void {
			if (this.timeline) {
				var tlTime:Number = (_pauseTime || _pauseTime == 0) ? _pauseTime : this.timeline.cachedTotalTime;
				if (this.cachedReversed) {
					var dur:Number = (this.cacheIsDirty) ? this.totalDuration : this.cachedTotalDuration;
					this.cachedStartTime = tlTime - ((dur - time) / this.cachedTimeScale);
				} else {
					this.cachedStartTime = tlTime - (time / this.cachedTimeScale);
				}
				if (!this.timeline.cacheIsDirty) { //for performance improvement. If the parent's cache is already dirty, it already took care of marking the anscestors as dirty too, so skip the function call here.
					setDirtyCache(false);
				}
				if (this.cachedTotalTime != time) {
					renderTime(time, suppressEvents, false);
				}
			}
		}
		
		
//---- GETTERS / SETTERS ------------------------------------------------------------
		
		/** 
		 * Length of time in seconds (or frames for frames-based tweens/timelines) before the tween should begin. 
		 * The tween's starting values are not determined until after the delay has expired (except in from() tweens) 
		 **/
		public function get delay():Number {
			return _delay;
		}
		
		public function set delay(n:Number):void {
			this.startTime += (n - _delay);
			_delay = n;
		}
		
		/**
		 * Duration of the tween in seconds (or frames for frames-based tweens/timelines) not including any repeats
		 * or repeatDelays. <code>totalDuration</code>, by contrast, does include repeats and repeatDelays.
		 **/
		public function get duration():Number {
			return this.cachedDuration;
		}
		
		public function set duration(n:Number):void {
			this.cachedDuration = this.cachedTotalDuration = n;
			setDirtyCache(false);
		}
		
		/**
		 * Duration of the tween in seconds (or frames for frames-based tweens/timelines) including any repeats
		 * or repeatDelays (which are only available on TweenMax and TimelineMax). <code>duration</code>, by contrast, does 
		 * <b>NOT</b> include repeats and repeatDelays. So if a TweenMax's <code>duration</code> is 1 and it has a repeat of 2, the <code>totalDuration</code> would be 3.
		 **/ 
		public function get totalDuration():Number {
			return this.cachedTotalDuration;
		}
		
		public function set totalDuration(n:Number):void {
			this.duration = n;
		}
		
		/**
		 * Most recently rendered time (or frame for frames-based tweens/timelines) according to its 
		 * <code>duration</code>. <code>totalTime</code>, by contrast, is based on its <code>totalDuration</code> 
		 * which includes repeats and repeatDelays. Since TweenLite and TimelineLite don't offer 
		 * <code>repeat</code> and <code>repeatDelay</code> functionality, <code>currentTime</code> 
		 * and <code>totalTime</code> will always be the same but in TweenMax or TimelineMax, they 
		 * could be different. For example, if a TimelineMax instance has a duration 
		 * of 5 a repeat of 1 (meaning its <code>totalDuration</code> is 10), at the end of the second cycle, 
		 * <code>currentTime</code> would be 5 whereas <code>totalTime</code> would be 10. If you tracked both
		 * properties over the course of the tween, you'd see <code>currentTime</code> go from 0 to 5 twice (one for each
		 * cycle) in the same time it takes <code>totalTime</code> go from 0 to 10.
		 **/
		public function get currentTime():Number {
			return this.cachedTime;
		}
		
		public function set currentTime(n:Number):void {
			setTotalTime(n, false);
		}
		
		/**
		 * Most recently rendered time (or frame for frames-based tweens/timelines) according to its 
		 * <code>totalDuration</code>. <code>currentTime</code>, by contrast, is based on its <code>duration</code> 
		 * which does NOT include repeats and repeatDelays. Since TweenLite and TimelineLite don't offer 
		 * <code>repeat</code> and <code>repeatDelay</code> functionality, <code>currentTime</code> 
		 * and <code>totalTime</code> will always be the same but in TweenMax or TimelineMax, they 
		 * could be different. For example, if a TimelineMax instance has a duration 
		 * of 5 a repeat of 1 (meaning its <code>totalDuration</code> is 10), at the end of the second cycle, 
		 * <code>currentTime</code> would be 5 whereas <code>totalTime</code> would be 10. If you tracked both
		 * properties over the course of the tween, you'd see <code>currentTime</code> go from 0 to 5 twice (one for each
		 * cycle) in the same time it takes <code>totalTime</code> go from 0 to 10.
		 **/
		public function get totalTime():Number {
			return this.cachedTotalTime;
		}
		
		public function set totalTime(n:Number):void {
			setTotalTime(n, false);
		}
		
		/** Start time in seconds (or frames for frames-based tweens/timelines), according to its position on its parent timeline **/
		public function get startTime():Number {
			return this.cachedStartTime;
		}
		
		public function set startTime(n:Number):void {
			var adjust:Boolean = Boolean(this.timeline != null && (n != this.cachedStartTime || this.gc));
			this.cachedStartTime = n;
			if (adjust) {
				this.timeline.addChild(this); //ensures that any necessary re-sequencing of TweenCores in the timeline occurs to make sure the rendering order is correct.
			}
		}
		
		/** Indicates the reversed state of the tween/timeline. This value is not affected by <code>yoyo</code> repeats and it does not take into account the reversed state of anscestor timelines. So for example, a tween that is not reversed might appear reversed if its parent timeline (or any ancenstor timeline) is reversed. **/
		public function get reversed():Boolean {
			return this.cachedReversed;
		}
		
		public function set reversed(b:Boolean):void {
			if (b != this.cachedReversed) {
				this.cachedReversed = b;
				setTotalTime(this.cachedTotalTime, true);
			}
		}
		
		/** Indicates the paused state of the tween/timeline. This does not take into account anscestor timelines. So for example, a tween that is not paused might appear paused if its parent timeline (or any ancenstor timeline) is paused. **/
		public function get paused():Boolean {
			return this.cachedPaused;
		}
		
		public function set paused(b:Boolean):void {
			if (b != this.cachedPaused && this.timeline) {
				if (b) {
					_pauseTime = this.timeline.rawTime;
				} else {
					this.cachedStartTime += this.timeline.rawTime - _pauseTime;
					_pauseTime = NaN;
					setDirtyCache(false);
				}
				this.cachedPaused = b;
				this.active = Boolean(!this.cachedPaused && this.cachedTotalTime > 0 && this.cachedTotalTime < this.cachedTotalDuration);
			}
			if (!b && this.gc) {
				this.setTotalTime(this.cachedTotalTime, false);
				this.setEnabled(true, false);
			}
		}

	}
}