Timeline.js 6.3 KB

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