transition.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. define("dojox/css3/transition", ["dojo/_base/kernel",
  2. "dojo/_base/lang",
  3. "dojo/_base/declare",
  4. "dojo/_base/array",
  5. "dojo/_base/Deferred",
  6. "dojo/DeferredList",
  7. "dojo/on",
  8. "dojo/_base/sniff"],
  9. function(dojo, lang, declare, array, deferred, deferredList, on, has){
  10. //TODO create cross platform animation/transition effects
  11. var transitionEndEventName = "transitionend";
  12. var transitionPrefix = "t"; //by default use "t" prefix and "ransition" to make word "transition"
  13. var translateMethodStart = "translate3d(";//Android 2.x does not support translateX in CSS Transition, we need to use translate3d in webkit browsers
  14. var translateMethodEnd = ",0,0)";
  15. if(has("webkit")){
  16. transitionPrefix = "WebkitT";
  17. transitionEndEventName = "webkitTransitionEnd";
  18. }else if(has("mozilla")){
  19. transitionPrefix = "MozT";
  20. translateMethodStart = "translateX(";
  21. translateMethodEnd = ")";
  22. }
  23. //TODO find a way to lock the animation and prevent animation conflict
  24. declare("dojox.css3.transition", null, {
  25. constructor: function(args){
  26. //default config should be in animation object itself instead of its prototype
  27. //otherwise, it might be easy for making mistake of modifying prototype
  28. var defaultConfig = {
  29. startState: {},
  30. endState: {},
  31. node: null,
  32. duration: 250,
  33. "in": true,
  34. direction: 1,
  35. autoClear: true
  36. };
  37. lang.mixin(this, defaultConfig);
  38. lang.mixin(this, args);
  39. //create the deferred object which will resolve after the animation is finished.
  40. //We can rely on "onAfterEnd" function to notify the end of a single animation,
  41. //but using a deferred object is easier to wait for multiple animations end.
  42. if(!this.deferred){
  43. this.deferred = new deferred();
  44. }
  45. },
  46. play: function(){
  47. //play the animation using CSS3 Transition
  48. dojox.css3.transition.groupedPlay([this]);
  49. },
  50. //method to apply the state of the transition
  51. _applyState: function(state){
  52. var style = this.node.style;
  53. for(var property in state){
  54. if(state.hasOwnProperty(property)){
  55. style[property] = state[property];
  56. }
  57. }
  58. },
  59. //method to initialize state for transition
  60. initState: function(){
  61. //apply the immediate style change for initial state.
  62. this.node.style[transitionPrefix + "ransitionProperty"] = "none";
  63. this.node.style[transitionPrefix + "ransitionDuration"] = "0ms";
  64. this._applyState(this.startState);
  65. },
  66. _beforeStart: function(){
  67. if (this.node.style.display === "none"){
  68. this.node.style.display = "";
  69. }
  70. this.beforeStart();
  71. },
  72. _beforeClear: function(){
  73. this.node.style[transitionPrefix + "ransitionProperty"] = null;
  74. this.node.style[transitionPrefix + "ransitionDuration"] = null;
  75. if(this["in"] !== true){
  76. this.node.style.display = "none";
  77. }
  78. this.beforeClear();
  79. },
  80. _onAfterEnd: function(){
  81. this.deferred.resolve(this.node);
  82. if(this.node.id && dojox.css3.transition.playing[this.node.id]===this.deferred){
  83. delete dojox.css3.transition.playing[this.node.id];
  84. }
  85. this.onAfterEnd();
  86. },
  87. beforeStart: function(){
  88. },
  89. beforeClear: function(){
  90. },
  91. onAfterEnd: function(){
  92. },
  93. //method to start the transition
  94. start: function(){
  95. this._beforeStart();
  96. var self = this;
  97. //change the transition duration
  98. self.node.style[transitionPrefix + "ransitionProperty"] = "all";
  99. self.node.style[transitionPrefix + "ransitionDuration"] = self.duration + "ms";
  100. //connect to clear the transition state after the transition end.
  101. //Since the transition is conducted asynchronously, we need to
  102. //connect to transition end event to clear the state
  103. on.once(self.node, transitionEndEventName, function(){
  104. self.clear();
  105. });
  106. this._applyState(this.endState);
  107. },
  108. //method to clear state after transition
  109. clear: function(){
  110. this._beforeClear();
  111. this._removeState(this.endState);
  112. console.log(this.node.id + " clear.");
  113. this._onAfterEnd();
  114. },
  115. //create removeState method
  116. _removeState: function(state){
  117. var style = this.node.style;
  118. for(var property in state){
  119. if(state.hasOwnProperty(property)){
  120. style[property] = null;
  121. }
  122. }
  123. }
  124. });
  125. //TODO add the lock mechanism for all of the transition effects
  126. // consider using only one object for one type of transition.
  127. //TODO create the first animation, slide.
  128. dojox.css3.transition.slide = function(node, config){
  129. //TODO create the return and set the startState, endState of the return
  130. var ret = new dojox.css3.transition(config);
  131. ret.node = node;
  132. var startX = "0";
  133. var endX = "0";
  134. if(ret["in"]){
  135. if(ret.direction === 1){
  136. startX = "100%";
  137. }else{
  138. startX = "-100%";
  139. }
  140. }else{
  141. if(ret.direction === 1){
  142. endX = "-100%";
  143. }else{
  144. endX = "100%";
  145. }
  146. }
  147. ret.startState[transitionPrefix + "ransform"]=translateMethodStart+startX+translateMethodEnd;
  148. ret.endState[transitionPrefix + "ransform"]=translateMethodStart+endX+translateMethodEnd;
  149. return ret;
  150. };
  151. //fade in/out animation effects
  152. dojox.css3.transition.fade = function(node, config){
  153. var ret = new dojox.css3.transition(config);
  154. ret.node = node;
  155. var startOpacity = "0";
  156. var endOpacity = "0";
  157. if(ret["in"]){
  158. endOpacity = "1";
  159. }else{
  160. startOpacity = "1";
  161. }
  162. lang.mixin(ret, {
  163. startState:{
  164. "opacity": startOpacity
  165. },
  166. endState:{
  167. "opacity": endOpacity
  168. }
  169. });
  170. return ret;
  171. };
  172. //fade in/out animation effects
  173. dojox.css3.transition.flip = function(node, config){
  174. var ret = new dojox.css3.transition(config);
  175. ret.node = node;
  176. if(ret["in"]){
  177. //Need to set opacity here because Android 2.2 has bug that
  178. //scale(...) in transform does not persist status
  179. lang.mixin(ret,{
  180. startState:{
  181. "opacity": "0"
  182. },
  183. endState:{
  184. "opacity": "1"
  185. }
  186. });
  187. ret.startState[transitionPrefix + "ransform"]="scale(0,0.8) skew(0,-30deg)";
  188. ret.endState[transitionPrefix + "ransform"]="scale(1,1) skew(0,0)";
  189. }else{
  190. lang.mixin(ret,{
  191. startState:{
  192. "opacity": "1"
  193. },
  194. endState:{
  195. "opacity": "0"
  196. }
  197. });
  198. ret.startState[transitionPrefix + "ransform"]="scale(1,1) skew(0,0)";
  199. ret.endState[transitionPrefix + "ransform"]="scale(0,0.8) skew(0,30deg)";
  200. }
  201. return ret;
  202. };
  203. var getWaitingList = function(/*Array*/ nodes){
  204. var defs = [];
  205. array.forEach(nodes, function(node){
  206. //check whether the node is under other animation
  207. if(node.id && dojox.css3.transition.playing[node.id]){
  208. //TODO hook on deferred object in dojox.css3.transition.playing
  209. defs.push(dojox.css3.transition.playing[node.id]);
  210. }
  211. });
  212. return new deferredList(defs);
  213. };
  214. dojox.css3.transition.getWaitingList = getWaitingList;
  215. //TODO groupedPlay should ensure the UI update happens when
  216. //all animations end.
  217. //the group player to start multiple animations together
  218. dojox.css3.transition.groupedPlay = function(/*Array*/args){
  219. //args should be array of dojox.css3.transition
  220. var animNodes = array.filter(args, function(item){
  221. return item.node;
  222. });
  223. var waitingList = getWaitingList(animNodes);
  224. //update registry with deferred objects in animations of args.
  225. array.forEach(args, function(item){
  226. if(item.node.id){
  227. dojox.css3.transition.playing[item.node.id] = item.deferred;
  228. }
  229. });
  230. //TODO wait for all deferred object in deferred list to resolve
  231. dojo.when(waitingList, function(){
  232. array.forEach(args, function(item){
  233. //set the start state
  234. item.initState();
  235. });
  236. //Assume the fps of the animation should be higher than 30 fps and
  237. //allow the browser to use one frame's time to redraw so that
  238. //the transition can be started
  239. setTimeout(function(){
  240. array.forEach(args, function(item){
  241. item.start();
  242. });
  243. }, 33);
  244. });
  245. };
  246. //the chain player to start multiple animations one by one
  247. dojox.css3.transition.chainedPlay = function(/*Array*/args){
  248. //args should be array of dojox.css3.transition
  249. var animNodes = array.filter(args, function(item){
  250. return item.node;
  251. });
  252. var waitingList = getWaitingList(animNodes);
  253. //update registry with deferred objects in animations of args.
  254. array.forEach(args, function(item){
  255. if(item.node.id){
  256. dojox.css3.transition.playing[item.node.id] = item.deferred;
  257. }
  258. });
  259. dojo.when(waitingList, function(){
  260. array.forEach(args, function(item){
  261. //set the start state
  262. item.initState();
  263. });
  264. //chain animations together
  265. for (var i=1, len=args.length; i < len; i++){
  266. args[i-1].deferred.then(lang.hitch(args[i], function(){
  267. this.start();
  268. }));
  269. }
  270. //Assume the fps of the animation should be higher than 30 fps and
  271. //allow the browser to use one frame's time to redraw so that
  272. //the transition can be started
  273. setTimeout(function(){
  274. args[0].start();
  275. }, 33);
  276. });
  277. };
  278. //TODO complete the registry mechanism for animation handling and prevent animation conflicts
  279. dojox.css3.transition.playing = {};
  280. return dojox.css3.transition;
  281. });