Timeline.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. define("dojox/fx/Timeline", ["dojo/_base/lang","dojo/fx/easing","dojo/_base/fx","dojo/dom","./_base","dojo/_base/connect",
  2. "dojo/_base/html", "dojo/_base/array","dojo/_base/Color"],
  3. function(lang, easingUtil, baseFx, dom, dojoxFx, connectUtil, htmlUtil, arrayUtil, Color){
  4. dojoxFx.animateTimeline = function(/* Object */options, /* DomNode|String */node){
  5. // options: Object
  6. // The paramters passed to the timeline animation. Includes:
  7. // keys: Array
  8. // An array of objects, with style properties and values.
  9. // duration:
  10. // Duration of the animation in milliseconds.
  11. // Defaults to 1000.
  12. // node: DomNode
  13. // The DomNode or id to be animated.
  14. //
  15. // summary:
  16. // An add-on to dojo.fx that provides the ability to create
  17. // a complex property animation based on an array of "keyframes".
  18. // description:
  19. // The Timeline is a replacement for the default dojo._Line.
  20. // Instead of _Line.getValue returning a float between 0-1,
  21. // _Timeline.getValue returns an object with all properties and
  22. // their current values.
  23. // A property does not have to appear in every keyframe.
  24. // As in the example below, "height" is transitioned from the first
  25. // keyframe to the third. "width" is transitioned from the first
  26. // to the second to the third.
  27. // Each keyframe can accept the following custom properties:
  28. // step: String
  29. // The start, finish or percentage that this keyframe represents.
  30. // Allowed parameters are:
  31. // 0%-100%
  32. // from (same as 0%, used to conform with the Webkit animation spec)
  33. // to (same as 100%, used to conform with the Webkit animation spec)
  34. // ease: String
  35. // The string name of a dojo.fx.easing ease. Defaults to "linear". Use
  36. // the suffix name of the ease, like: "quadIn", not: "dojo.fx.quadIn".
  37. //
  38. // example:
  39. // | var keys = [
  40. // | {
  41. // | step:"0px",
  42. // | ease:"quadInOut",
  43. // | width:"50px",
  44. // | height:"50px",
  45. // | },{
  46. // | step:"25%",
  47. // | width:"190px"
  48. // | },{
  49. // | step:"100%",
  50. // | width:"10px",
  51. // | height:"200px",
  52. // | }
  53. // | ];
  54. // | ani = dojox.fx.animateTimeline({keys:keys, duration:2000}, "myDiv").play();
  55. //
  56. var _curve = new Timeline(options.keys);
  57. var ani = baseFx.animateProperty({
  58. node:dom.byId(node || options.node),
  59. duration:options.duration || 1000,
  60. properties:_curve._properties,
  61. // don't change! This easing is for the timeline,
  62. // not individual properties
  63. easing:easingUtil.linear,
  64. onAnimate: function(v){
  65. //console.log(" ani:", v);
  66. }
  67. });
  68. connectUtil.connect(ani, "onEnd", function(node){
  69. // Setting the final style. Hiccups in the browser
  70. // can cause the animation to lose track. This ensures
  71. // that it finishes in the proper location.
  72. var sty = ani.curve.getValue(ani.reversed ? 0 : 1);
  73. htmlUtil.style(node, sty);
  74. });
  75. connectUtil.connect(ani, "beforeBegin", function(){
  76. // remove default curve and replace it with Timeline
  77. if(ani.curve){ delete ani.curve; }
  78. ani.curve = _curve;
  79. _curve.ani = ani;
  80. })
  81. return ani; // dojo.Animation
  82. }
  83. var Timeline = function(/* Array */keys){
  84. // summary:
  85. // The dojox.fx._Timeline object from which an instance
  86. // is created
  87. // tags:
  88. // private
  89. this.keys = lang.isArray(keys) ? this.flatten(keys) : keys;
  90. }
  91. Timeline.prototype.flatten = function(keys){
  92. // summary:
  93. // An internally used function that converts the keyframes
  94. // as used in the example above into a series of key values
  95. // which is what is used in the animation parsing.
  96. var getPercent = function(str, idx){
  97. if(str == "from"){ return 0; }
  98. if(str == "to"){ return 1; }
  99. if(str === undefined){
  100. return idx==0 ? 0 : idx / (keys.length - 1)
  101. }
  102. return parseInt(str, 10) * .01
  103. }
  104. var p = {}, o = {};
  105. arrayUtil.forEach(keys, function(k, i){
  106. var step = getPercent(k.step, i);
  107. var ease = easingUtil[k.ease] || easingUtil.linear;
  108. for(var nm in k){
  109. if(nm == "step" || nm == "ease" || nm == "from" || nm == "to"){ continue; }
  110. if(!o[nm]){
  111. o[nm] = {
  112. steps:[],
  113. values:[],
  114. eases:[],
  115. ease:ease
  116. };
  117. p[nm] = {};
  118. if(!/#/.test(k[nm])){
  119. p[nm].units = o[nm].units = /\D{1,}/.exec(k[nm]).join("");
  120. }else{
  121. p[nm].units = o[nm].units = "isColor";
  122. }
  123. }
  124. o[nm].eases.push(easingUtil[k.ease || "linear"]);
  125. o[nm].steps.push(step);
  126. if(p[nm].units == "isColor"){
  127. o[nm].values.push(new Color(k[nm]));
  128. }else{
  129. o[nm].values.push(parseInt(/\d{1,}/.exec(k[nm]).join("")));
  130. }
  131. if(p[nm].start === undefined){
  132. p[nm].start = o[nm].values[o[nm].values.length-1];
  133. }else{
  134. p[nm].end = o[nm].values[o[nm].values.length-1]
  135. }
  136. }
  137. });
  138. this._properties = p;
  139. return o; // Object
  140. }
  141. Timeline.prototype.getValue = function(/*float*/ p){
  142. // summary:
  143. // Replaces the native getValue in dojo.fx.Animation.
  144. // Returns an object with all propeties used in the animation
  145. // and the property's current value
  146. p = this.ani._reversed ? 1-p : p;
  147. var o = {}, self = this;
  148. var getProp = function(nm, i){
  149. return self._properties[nm].units!="isColor" ?
  150. self.keys[nm].values[i] + self._properties[nm].units :
  151. self.keys[nm].values[i].toCss();
  152. }
  153. for(var nm in this.keys){
  154. var k = this.keys[nm];
  155. for(var i=0; i<k.steps.length; i++){
  156. var step = k.steps[i];
  157. var ns = k.steps[i+1];
  158. var next = i < k.steps.length ? true : false;
  159. var ease = k.eases[i] || function(n){return n;};
  160. if(p == step){
  161. // first or last
  162. o[nm] = getProp(nm, i);
  163. if(!next || (next && this.ani._reversed)) break;
  164. }else if(p > step){
  165. if(next && p < k.steps[i+1]){
  166. // inbetween steps
  167. var end = k.values[i+1];
  168. var beg = k.values[i];
  169. var seg = (1 / (ns - step)) * (p - step);
  170. seg = ease(seg);
  171. if(beg instanceof Color){
  172. o[nm] = Color.blendColors(beg, end, seg).toCss(false);
  173. }else{
  174. var df = end - beg;
  175. o[nm] = beg + seg * df + this._properties[nm].units;
  176. }
  177. break;
  178. }else{
  179. // completed keys before 100%
  180. o[nm] = getProp(nm, i);
  181. }
  182. }else if((next && !this.ani._reversed) || (!next && this.ani._reversed)){
  183. o[nm] = getProp(nm, i);
  184. }
  185. }
  186. }
  187. return o; // Object
  188. };
  189. dojoxFx._Timeline = Timeline;
  190. return dojoxFx;
  191. });