fx.js 12 KB

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