PageRenderTime 142ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/charts/as/com/yahoo/astra/animation/Animation.as

https://github.com/sdesai/yui2
ActionScript | 555 lines | 232 code | 59 blank | 264 comment | 21 complexity | 07dfb90bce27c1f6c3a819399755f4a9 MD5 | raw file
  1. package com.yahoo.astra.animation
  2. {
  3. import flash.events.EventDispatcher;
  4. import flash.events.TimerEvent;
  5. import flash.utils.Dictionary;
  6. import flash.utils.Timer;
  7. import flash.utils.getTimer;
  8. //--------------------------------------
  9. // Events
  10. //--------------------------------------
  11. /**
  12. * Dispatched when the Animation instance starts.
  13. *
  14. * @eventType com.yahoo.astra.animation.AnimationEvent.START
  15. */
  16. [Event(name="start", type="com.yahoo.astra.animation.AnimationEvent")]
  17. /**
  18. * Dispatched when the Animation instance has changed.
  19. *
  20. * @eventType com.yahoo.astra.animation.AnimationEvent.UPDATE
  21. */
  22. [Event(name="update", type="com.yahoo.astra.animation.AnimationEvent")]
  23. /**
  24. * Dispatched when the Animation instance has finished.
  25. *
  26. * @eventType com.yahoo.astra.animation.AnimationEvent.COMPLETE
  27. */
  28. [Event(name="complete", type="com.yahoo.astra.animation.AnimationEvent")]
  29. /**
  30. * Dispatched when the Animation instance is paused.
  31. *
  32. * @eventType com.yahoo.astra.animation.AnimationEvent.PAUSE
  33. */
  34. [Event(name="pause", type="com.yahoo.astra.animation.AnimationEvent")]
  35. /**
  36. * An ultra lightweight animation engine.
  37. *
  38. * @example The following code animates a Shape from its current location to a new location over a period of two seconds:
  39. * <listing version="3.0">
  40. * // create the square
  41. * var square:Shape = new Shape();
  42. * square.graphics.beginFill( 0xcccccc );
  43. * square.graphics.drawRect( 0, 0, 20, 20 );
  44. * square.graphics.endFill();
  45. * square.x = 20;
  46. * square.y = 20;
  47. * this.addChild( square );
  48. *
  49. * // animate the square's position
  50. * var animation:Animation = Animation.create( square, 2000, { x: 100, y: 200 } );
  51. * </listing>
  52. *
  53. * @example The following code will draw a circle and use an Animation instance
  54. * to change its alpha property from 0.0 to 1.0 over a period of 1.5 seconds.
  55. * It will set the easingFunction property to <code>Back.easeOut</code>, which
  56. * is an easing function included with Flash CS3. In order to implement this
  57. * example, you will need to save this code as a class file and set it as the
  58. * Document Class of your flash application.
  59. *
  60. * <listing version="3.0">
  61. * package
  62. * {
  63. * import fl.motion.easing.Back;
  64. * import flash.display.Shape;
  65. * import flash.display.Sprite;
  66. * import com.yahoo.astra.animation.Animation;
  67. * import com.yahoo.astra.animation.AnimationEvent;
  68. *
  69. * public class AnimationExample extends Sprite
  70. * {
  71. * public function AnimationExample()
  72. * {
  73. * // Create a simple circular display object
  74. * this.circle = new Shape();
  75. * this.circle.graphics.beginFill(0xcccccc);
  76. * this.circle.graphics.drawEllipse(0, 0, 50, 50);
  77. * this.circle.graphics.endFill();
  78. * this.addChild(circle);
  79. *
  80. * // Create the instance animating over 1500ms from 0 to 1
  81. * this.animation = new Animation( 1500, { alpha: 0.0 }, { alpha: 1.0 } );
  82. *
  83. * // Use an easing equation
  84. * this.animation.easingFunction = Back.easeOut;
  85. *
  86. * // Listen for events to update our circle's values
  87. * this.animation.addEventListener( AnimationEvent.UPDATE, animationUpdateHandler );
  88. * this.animation.addEventListener( AnimationEvent.COMPLETE, animationCompleteHandler );
  89. * }
  90. *
  91. * // Should be a member variable so that the garbage collector doesn't
  92. * // remove the instance from memory before it finishes
  93. * private var animation:Animation;
  94. *
  95. * // The display object whose properties we will animate
  96. * private var circle:Shape;
  97. *
  98. * private function animationUpdateHandler(event:AnimationEvent):void
  99. * {
  100. * this.circle.alpha = event.parameters.alpha;
  101. * }
  102. *
  103. * private function animationCompleteHandler(event:AnimationEvent):void
  104. * {
  105. * this.animationUpdateHandler(event);
  106. *
  107. * // Set the animation instance to null to ensure garbage collection
  108. * this.animation = null;
  109. * }
  110. * }
  111. * }
  112. * </listing>
  113. * @author Josh Tynjala
  114. */
  115. public class Animation extends EventDispatcher
  116. {
  117. //--------------------------------------
  118. // Class Properties
  119. //--------------------------------------
  120. /**
  121. * @private
  122. * Hash to get an Animation's target.
  123. */
  124. private static var animationToTarget:Dictionary = new Dictionary();
  125. /**
  126. * @private
  127. * Hash to get the a target's Animation.
  128. */
  129. private static var targetToAnimations:Dictionary = new Dictionary();
  130. /**
  131. * @private
  132. * The main timer shared by all Animation instances.
  133. */
  134. private static var mainTimer:Timer = new Timer(10);
  135. //--------------------------------------
  136. // Class Methods
  137. //--------------------------------------
  138. /**
  139. * Animates one or more properties of a target object. Uses the current values
  140. * of these properties as the starting values.
  141. *
  142. * @param target the object whose properties will be animated.
  143. * @param duration the time in milliseconds over which the properties will be animated.
  144. * @param parameters an object containing keys of property names on the object and the ending values.
  145. * @param autoStart if true (the default), the animation will begin automatically.
  146. * if false, the returned Animation object will not automatically begin, and
  147. * one must call the <code>start()</code> function to make it run.
  148. * @param clearAllRunning If true, all other animations started with <code>create()</code> for this target will be cleared.
  149. *
  150. * @return The newly-created Animation instance
  151. */
  152. public static function create(target:Object, duration:int, parameters:Object, autoStart:Boolean = true, clearAllRunning:Boolean = false):Animation
  153. {
  154. var animations:Array = targetToAnimations[target] as Array;
  155. if(!animations)
  156. {
  157. animations = [];
  158. targetToAnimations[target] = animations;
  159. }
  160. //if requested, stop all other animations running for this target
  161. if(clearAllRunning && animations.length > 0)
  162. {
  163. var animationCount:int = animations.length;
  164. for(var i:int = 0; i < animationCount; i++)
  165. {
  166. var oldAnimation:Animation = Animation(animations[i]);
  167. //stop it at the current position
  168. oldAnimation.pause();
  169. removeAnimation(oldAnimation);
  170. }
  171. }
  172. //create the start parameters from the existing properties
  173. var startParameters:Object = {};
  174. for(var prop:String in parameters)
  175. {
  176. if(target.hasOwnProperty(prop))
  177. {
  178. startParameters[prop] = target[prop];
  179. }
  180. else startParameters[prop] = 0;
  181. }
  182. //create the Animation instance
  183. var animation:Animation = new Animation(duration, startParameters, parameters, autoStart);
  184. animation.addEventListener(AnimationEvent.UPDATE, tweenUpdateHandler);
  185. animation.addEventListener(AnimationEvent.COMPLETE, tweenCompleteHandler);
  186. animations.push(animation);
  187. //reference the target so that we may remove the animation later
  188. animationToTarget[animation] = target;
  189. return animation;
  190. }
  191. /**
  192. * Immediately destroys an animation instantiated with <code>create()</code>.
  193. */
  194. public static function kill(animation:Animation):void
  195. {
  196. if(!animation)
  197. {
  198. return;
  199. }
  200. if(animation.active)
  201. {
  202. animation.pause();
  203. }
  204. removeAnimation(animation);
  205. }
  206. /**
  207. * @private
  208. * Handles updating the properties on a Animation target.
  209. */
  210. private static function tweenUpdateHandler(event:AnimationEvent):void
  211. {
  212. var animation:Animation = Animation(event.target);
  213. var target:Object = animationToTarget[animation];
  214. var updatedParameters:Object = event.parameters;
  215. for(var prop:String in updatedParameters)
  216. {
  217. if(target.hasOwnProperty(prop))
  218. {
  219. target[prop] = updatedParameters[prop];
  220. }
  221. }
  222. }
  223. /**
  224. * @private
  225. * Completes a tween for a Animation target.
  226. */
  227. private static function tweenCompleteHandler(event:AnimationEvent):void
  228. {
  229. tweenUpdateHandler(event);
  230. var animation:Animation = Animation(event.target);
  231. //if the animation is active, that means it has been restarted
  232. //and we can leave it running. our listeners will still be valid.
  233. if(!animation.active)
  234. {
  235. removeAnimation(animation);
  236. }
  237. }
  238. /**
  239. * @private
  240. * Removes an Animation and its target from management.
  241. */
  242. private static function removeAnimation(animation:Animation):void
  243. {
  244. if(!animation)
  245. {
  246. return;
  247. }
  248. var target:Object = animationToTarget[animation];
  249. animationToTarget[animation] = null;
  250. if(target)
  251. {
  252. //remove the reference to the animation
  253. var animations:Array = targetToAnimations[target] as Array;
  254. var index:int = animations.indexOf(animation);
  255. animations.splice(index, 1);
  256. }
  257. animation.removeEventListener(AnimationEvent.UPDATE, tweenUpdateHandler);
  258. animation.removeEventListener(AnimationEvent.COMPLETE, tweenCompleteHandler);
  259. }
  260. /**
  261. * @private
  262. * Animation uses a single global Timer to save CPU time. This function lets each
  263. * individual instance listen for the timer's events.
  264. */
  265. private static function startListenToTimer(handler:Function):void
  266. {
  267. Animation.mainTimer.addEventListener(TimerEvent.TIMER, handler, false, 0, true);
  268. //if this is the first listener, start the timer
  269. if(!Animation.mainTimer.running)
  270. {
  271. Animation.mainTimer.start();
  272. }
  273. }
  274. /**
  275. * @private
  276. * Animation uses a single global Timer to save CPU time. This function lets each
  277. * individual instance stop listening for the timer's events.
  278. */
  279. private static function stopListenToTimer(handler:Function):void
  280. {
  281. Animation.mainTimer.removeEventListener(TimerEvent.TIMER, handler);
  282. //if the timer doesn't have any more listeners, we don't need to keep it running
  283. if(!Animation.mainTimer.hasEventListener(TimerEvent.TIMER))
  284. {
  285. Animation.mainTimer.stop();
  286. }
  287. }
  288. //--------------------------------------
  289. // Constructor
  290. //--------------------------------------
  291. /**
  292. * Constructor.
  293. *
  294. * @param duration the time in milliseconds that the tween will run
  295. * @param start the starting values of the tween
  296. * @param end the ending values of the tween
  297. * @param autoStart if false, the tween will not run until start() is called
  298. */
  299. public function Animation(duration:int, start:Object, end:Object, autoStart:Boolean = true)
  300. {
  301. super();
  302. this._duration = duration;
  303. this._startParameters = start;
  304. this.endParameters = end;
  305. if(autoStart)
  306. {
  307. this.start();
  308. }
  309. }
  310. //--------------------------------------
  311. // Properties
  312. //--------------------------------------
  313. /**
  314. * @private
  315. * Storage for the active property.
  316. */
  317. private var _active:Boolean = false;
  318. /**
  319. * If true, the animation is currently running.
  320. */
  321. public function get active():Boolean
  322. {
  323. return this._active;
  324. }
  325. /**
  326. * @private
  327. * The time at which the animation last started running. If it has been paused
  328. * one or more times, this value is reset to the restart time.
  329. */
  330. private var _startTime:int;
  331. /**
  332. * @private
  333. * If the animation is paused, the running time is saved here.
  334. */
  335. private var _savedRuntime:int;
  336. /**
  337. * @private
  338. * Storage for the duration property.
  339. */
  340. private var _duration:int;
  341. /**
  342. * The duration in milliseconds that the animation will run.
  343. */
  344. public function get duration():int
  345. {
  346. return this._duration;
  347. }
  348. /**
  349. * @private
  350. * Storage for the starting values.
  351. */
  352. private var _startParameters:Object;
  353. /**
  354. * @private
  355. * Storage for the ending values.
  356. */
  357. private var _endParameters:Object;
  358. /**
  359. * @private
  360. * Used to determine the "ranges" between starting and ending values.
  361. */
  362. protected function get endParameters():Object
  363. {
  364. return this._endParameters;
  365. }
  366. /**
  367. * @private
  368. */
  369. protected function set endParameters(value:Object):void
  370. {
  371. this._ranges = {};
  372. for(var prop:String in value)
  373. {
  374. var startValue:Number = Number(this._startParameters[prop]);
  375. var endValue:Number = Number(value[prop]);
  376. var range:Number = endValue - startValue;
  377. this._ranges[prop] = range;
  378. }
  379. this._endParameters = value;
  380. }
  381. /**
  382. * @private
  383. * The difference between the startParameters and endParameters values.
  384. */
  385. private var _ranges:Object;
  386. /**
  387. * @private
  388. * Storage for the easingFunction property.
  389. */
  390. private var _easingFunction:Function = function(t:Number, b:Number, c:Number, d:Number):Number
  391. {
  392. return c * t / d + b;
  393. }
  394. /**
  395. * The easing function which is used with the tween.
  396. */
  397. public function get easingFunction():Function
  398. {
  399. return this._easingFunction;
  400. }
  401. /**
  402. * @private
  403. */
  404. public function set easingFunction(value:Function):void
  405. {
  406. this._easingFunction = value;
  407. }
  408. //--------------------------------------
  409. // Public Methods
  410. //--------------------------------------
  411. /**
  412. * Starts the tween. Should be used to restart a paused tween, or to
  413. * start a new tween with autoStart disabled.
  414. */
  415. public function start():void
  416. {
  417. Animation.startListenToTimer(this.timerUpdateHandler);
  418. this._startTime = getTimer();
  419. this._active = true;
  420. this.dispatchEvent(new AnimationEvent(AnimationEvent.START, this._startParameters));
  421. this.dispatchEvent(new AnimationEvent(AnimationEvent.UPDATE, this._startParameters));
  422. }
  423. /**
  424. * Pauses a tween so that it may be restarted again with the same
  425. * timing.
  426. */
  427. public function pause():void
  428. {
  429. Animation.stopListenToTimer(this.timerUpdateHandler);
  430. this._savedRuntime += getTimer() - this._startTime;
  431. this._active = false;
  432. this.dispatchEvent(new AnimationEvent(AnimationEvent.PAUSE, update(this._savedRuntime)));
  433. }
  434. /**
  435. * Swaps the start and end parameters and restarts the animation.
  436. */
  437. public function yoyo():void
  438. {
  439. this.pause();
  440. this._savedRuntime = 0;
  441. var temp:Object = this._startParameters;
  442. this._startParameters = this.endParameters;
  443. this.endParameters = temp;
  444. this.start();
  445. }
  446. /**
  447. * Forces a tween to its completion values.
  448. */
  449. public function end():void
  450. {
  451. Animation.stopListenToTimer(this.timerUpdateHandler);
  452. this._active = false;
  453. this.dispatchEvent(new AnimationEvent(AnimationEvent.COMPLETE, this.endParameters));
  454. }
  455. //--------------------------------------
  456. // Private Methods
  457. //--------------------------------------
  458. /**
  459. * @private
  460. */
  461. private function timerUpdateHandler(event:TimerEvent):void
  462. {
  463. var runtime:int = this._savedRuntime + getTimer() - this._startTime;
  464. if(runtime >= this._duration)
  465. {
  466. this.end();
  467. return;
  468. }
  469. this.dispatchEvent(new AnimationEvent(AnimationEvent.UPDATE, this.update(runtime)));
  470. }
  471. /**
  472. * @private
  473. * Generates updated values for the animation based on the current time.
  474. */
  475. private function update(runtime:int):Object
  476. {
  477. //can easily handle parameters as hashes or Arrays.
  478. var updated:Object;
  479. if(this._startParameters is Array)
  480. {
  481. updated = [];
  482. }
  483. else
  484. {
  485. updated = {};
  486. }
  487. for(var prop:String in this._ranges)
  488. {
  489. var startValue:Number = this._startParameters[prop] as Number;
  490. var range:Number = this._ranges[prop];
  491. updated[prop] = this._easingFunction(runtime, startValue, range, this._duration);
  492. }
  493. return updated;
  494. }
  495. }
  496. }