fx.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  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["dojo.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojo.fx"] = true;
  8. dojo.provide("dojo.fx");
  9. dojo.require("dojo.fx.Toggler");
  10. /*=====
  11. dojo.fx = {
  12. // summary: Effects library on top of Base animations
  13. };
  14. =====*/
  15. (function(){
  16. var d = dojo,
  17. _baseObj = {
  18. _fire: function(evt, args){
  19. if(this[evt]){
  20. this[evt].apply(this, args||[]);
  21. }
  22. return this;
  23. }
  24. };
  25. var _chain = function(animations){
  26. this._index = -1;
  27. this._animations = animations||[];
  28. this._current = this._onAnimateCtx = this._onEndCtx = null;
  29. this.duration = 0;
  30. d.forEach(this._animations, function(a){
  31. this.duration += a.duration;
  32. if(a.delay){ this.duration += a.delay; }
  33. }, this);
  34. };
  35. d.extend(_chain, {
  36. _onAnimate: function(){
  37. this._fire("onAnimate", arguments);
  38. },
  39. _onEnd: function(){
  40. d.disconnect(this._onAnimateCtx);
  41. d.disconnect(this._onEndCtx);
  42. this._onAnimateCtx = this._onEndCtx = null;
  43. if(this._index + 1 == this._animations.length){
  44. this._fire("onEnd");
  45. }else{
  46. // switch animations
  47. this._current = this._animations[++this._index];
  48. this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate");
  49. this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd");
  50. this._current.play(0, true);
  51. }
  52. },
  53. play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
  54. if(!this._current){ this._current = this._animations[this._index = 0]; }
  55. if(!gotoStart && this._current.status() == "playing"){ return this; }
  56. var beforeBegin = d.connect(this._current, "beforeBegin", this, function(){
  57. this._fire("beforeBegin");
  58. }),
  59. onBegin = d.connect(this._current, "onBegin", this, function(arg){
  60. this._fire("onBegin", arguments);
  61. }),
  62. onPlay = d.connect(this._current, "onPlay", this, function(arg){
  63. this._fire("onPlay", arguments);
  64. d.disconnect(beforeBegin);
  65. d.disconnect(onBegin);
  66. d.disconnect(onPlay);
  67. });
  68. if(this._onAnimateCtx){
  69. d.disconnect(this._onAnimateCtx);
  70. }
  71. this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate");
  72. if(this._onEndCtx){
  73. d.disconnect(this._onEndCtx);
  74. }
  75. this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd");
  76. this._current.play.apply(this._current, arguments);
  77. return this;
  78. },
  79. pause: function(){
  80. if(this._current){
  81. var e = d.connect(this._current, "onPause", this, function(arg){
  82. this._fire("onPause", arguments);
  83. d.disconnect(e);
  84. });
  85. this._current.pause();
  86. }
  87. return this;
  88. },
  89. gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
  90. this.pause();
  91. var offset = this.duration * percent;
  92. this._current = null;
  93. d.some(this._animations, function(a){
  94. if(a.duration <= offset){
  95. this._current = a;
  96. return true;
  97. }
  98. offset -= a.duration;
  99. return false;
  100. });
  101. if(this._current){
  102. this._current.gotoPercent(offset / this._current.duration, andPlay);
  103. }
  104. return this;
  105. },
  106. stop: function(/*boolean?*/ gotoEnd){
  107. if(this._current){
  108. if(gotoEnd){
  109. for(; this._index + 1 < this._animations.length; ++this._index){
  110. this._animations[this._index].stop(true);
  111. }
  112. this._current = this._animations[this._index];
  113. }
  114. var e = d.connect(this._current, "onStop", this, function(arg){
  115. this._fire("onStop", arguments);
  116. d.disconnect(e);
  117. });
  118. this._current.stop();
  119. }
  120. return this;
  121. },
  122. status: function(){
  123. return this._current ? this._current.status() : "stopped";
  124. },
  125. destroy: function(){
  126. if(this._onAnimateCtx){ d.disconnect(this._onAnimateCtx); }
  127. if(this._onEndCtx){ d.disconnect(this._onEndCtx); }
  128. }
  129. });
  130. d.extend(_chain, _baseObj);
  131. dojo.fx.chain = function(/*dojo.Animation[]*/ animations){
  132. // summary:
  133. // Chain a list of `dojo.Animation`s to run in sequence
  134. //
  135. // description:
  136. // Return a `dojo.Animation` which will play all passed
  137. // `dojo.Animation` instances in sequence, firing its own
  138. // synthesized events simulating a single animation. (eg:
  139. // onEnd of this animation means the end of the chain,
  140. // not the individual animations within)
  141. //
  142. // example:
  143. // Once `node` is faded out, fade in `otherNode`
  144. // | dojo.fx.chain([
  145. // | dojo.fadeIn({ node:node }),
  146. // | dojo.fadeOut({ node:otherNode })
  147. // | ]).play();
  148. //
  149. return new _chain(animations) // dojo.Animation
  150. };
  151. var _combine = function(animations){
  152. this._animations = animations||[];
  153. this._connects = [];
  154. this._finished = 0;
  155. this.duration = 0;
  156. d.forEach(animations, function(a){
  157. var duration = a.duration;
  158. if(a.delay){ duration += a.delay; }
  159. if(this.duration < duration){ this.duration = duration; }
  160. this._connects.push(d.connect(a, "onEnd", this, "_onEnd"));
  161. }, this);
  162. this._pseudoAnimation = new d.Animation({curve: [0, 1], duration: this.duration});
  163. var self = this;
  164. d.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
  165. function(evt){
  166. self._connects.push(d.connect(self._pseudoAnimation, evt,
  167. function(){ self._fire(evt, arguments); }
  168. ));
  169. }
  170. );
  171. };
  172. d.extend(_combine, {
  173. _doAction: function(action, args){
  174. d.forEach(this._animations, function(a){
  175. a[action].apply(a, args);
  176. });
  177. return this;
  178. },
  179. _onEnd: function(){
  180. if(++this._finished > this._animations.length){
  181. this._fire("onEnd");
  182. }
  183. },
  184. _call: function(action, args){
  185. var t = this._pseudoAnimation;
  186. t[action].apply(t, args);
  187. },
  188. play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
  189. this._finished = 0;
  190. this._doAction("play", arguments);
  191. this._call("play", arguments);
  192. return this;
  193. },
  194. pause: function(){
  195. this._doAction("pause", arguments);
  196. this._call("pause", arguments);
  197. return this;
  198. },
  199. gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
  200. var ms = this.duration * percent;
  201. d.forEach(this._animations, function(a){
  202. a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay);
  203. });
  204. this._call("gotoPercent", arguments);
  205. return this;
  206. },
  207. stop: function(/*boolean?*/ gotoEnd){
  208. this._doAction("stop", arguments);
  209. this._call("stop", arguments);
  210. return this;
  211. },
  212. status: function(){
  213. return this._pseudoAnimation.status();
  214. },
  215. destroy: function(){
  216. d.forEach(this._connects, dojo.disconnect);
  217. }
  218. });
  219. d.extend(_combine, _baseObj);
  220. dojo.fx.combine = function(/*dojo.Animation[]*/ animations){
  221. // summary:
  222. // Combine a list of `dojo.Animation`s to run in parallel
  223. //
  224. // description:
  225. // Combine an array of `dojo.Animation`s to run in parallel,
  226. // providing a new `dojo.Animation` instance encompasing each
  227. // animation, firing standard animation events.
  228. //
  229. // example:
  230. // Fade out `node` while fading in `otherNode` simultaneously
  231. // | dojo.fx.combine([
  232. // | dojo.fadeIn({ node:node }),
  233. // | dojo.fadeOut({ node:otherNode })
  234. // | ]).play();
  235. //
  236. // example:
  237. // When the longest animation ends, execute a function:
  238. // | var anim = dojo.fx.combine([
  239. // | dojo.fadeIn({ node: n, duration:700 }),
  240. // | dojo.fadeOut({ node: otherNode, duration: 300 })
  241. // | ]);
  242. // | dojo.connect(anim, "onEnd", function(){
  243. // | // overall animation is done.
  244. // | });
  245. // | anim.play(); // play the animation
  246. //
  247. return new _combine(animations); // dojo.Animation
  248. };
  249. dojo.fx.wipeIn = function(/*Object*/ args){
  250. // summary:
  251. // Expand a node to it's natural height.
  252. //
  253. // description:
  254. // Returns an animation that will expand the
  255. // node defined in 'args' object from it's current height to
  256. // it's natural height (with no scrollbar).
  257. // Node must have no margin/border/padding.
  258. //
  259. // args: Object
  260. // A hash-map of standard `dojo.Animation` constructor properties
  261. // (such as easing: node: duration: and so on)
  262. //
  263. // example:
  264. // | dojo.fx.wipeIn({
  265. // | node:"someId"
  266. // | }).play()
  267. var node = args.node = d.byId(args.node), s = node.style, o;
  268. var anim = d.animateProperty(d.mixin({
  269. properties: {
  270. height: {
  271. // wrapped in functions so we wait till the last second to query (in case value has changed)
  272. start: function(){
  273. // start at current [computed] height, but use 1px rather than 0
  274. // because 0 causes IE to display the whole panel
  275. o = s.overflow;
  276. s.overflow = "hidden";
  277. if(s.visibility == "hidden" || s.display == "none"){
  278. s.height = "1px";
  279. s.display = "";
  280. s.visibility = "";
  281. return 1;
  282. }else{
  283. var height = d.style(node, "height");
  284. return Math.max(height, 1);
  285. }
  286. },
  287. end: function(){
  288. return node.scrollHeight;
  289. }
  290. }
  291. }
  292. }, args));
  293. d.connect(anim, "onEnd", function(){
  294. s.height = "auto";
  295. s.overflow = o;
  296. });
  297. return anim; // dojo.Animation
  298. };
  299. dojo.fx.wipeOut = function(/*Object*/ args){
  300. // summary:
  301. // Shrink a node to nothing and hide it.
  302. //
  303. // description:
  304. // Returns an animation that will shrink node defined in "args"
  305. // from it's current height to 1px, and then hide it.
  306. //
  307. // args: Object
  308. // A hash-map of standard `dojo.Animation` constructor properties
  309. // (such as easing: node: duration: and so on)
  310. //
  311. // example:
  312. // | dojo.fx.wipeOut({ node:"someId" }).play()
  313. var node = args.node = d.byId(args.node), s = node.style, o;
  314. var anim = d.animateProperty(d.mixin({
  315. properties: {
  316. height: {
  317. end: 1 // 0 causes IE to display the whole panel
  318. }
  319. }
  320. }, args));
  321. d.connect(anim, "beforeBegin", function(){
  322. o = s.overflow;
  323. s.overflow = "hidden";
  324. s.display = "";
  325. });
  326. d.connect(anim, "onEnd", function(){
  327. s.overflow = o;
  328. s.height = "auto";
  329. s.display = "none";
  330. });
  331. return anim; // dojo.Animation
  332. };
  333. dojo.fx.slideTo = function(/*Object*/ args){
  334. // summary:
  335. // Slide a node to a new top/left position
  336. //
  337. // description:
  338. // Returns an animation that will slide "node"
  339. // defined in args Object from its current position to
  340. // the position defined by (args.left, args.top).
  341. //
  342. // args: Object
  343. // A hash-map of standard `dojo.Animation` constructor properties
  344. // (such as easing: node: duration: and so on). Special args members
  345. // are `top` and `left`, which indicate the new position to slide to.
  346. //
  347. // example:
  348. // | dojo.fx.slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
  349. var node = args.node = d.byId(args.node),
  350. top = null, left = null;
  351. var init = (function(n){
  352. return function(){
  353. var cs = d.getComputedStyle(n);
  354. var pos = cs.position;
  355. top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
  356. left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
  357. if(pos != 'absolute' && pos != 'relative'){
  358. var ret = d.position(n, true);
  359. top = ret.y;
  360. left = ret.x;
  361. n.style.position="absolute";
  362. n.style.top=top+"px";
  363. n.style.left=left+"px";
  364. }
  365. };
  366. })(node);
  367. init();
  368. var anim = d.animateProperty(d.mixin({
  369. properties: {
  370. top: args.top || 0,
  371. left: args.left || 0
  372. }
  373. }, args));
  374. d.connect(anim, "beforeBegin", anim, init);
  375. return anim; // dojo.Animation
  376. };
  377. })();
  378. }