af.css3animate.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /**
  2. * af.css3animate - a css3 animation library that supports chaning/callbacks
  3. * Copyright 2013 - Intel
  4. */ (function($) {
  5. var cache = [];
  6. var objId = function(obj) {
  7. if (!obj.afCSS3AnimateId) obj.afCSS3AnimateId = $.uuid();
  8. return obj.afCSS3AnimateId;
  9. };
  10. var getEl = function(elID) {
  11. if (typeof elID == "string" || elID instanceof String) {
  12. return document.getElementById(elID);
  13. } else if ($.is$(elID)) {
  14. return elID[0];
  15. } else {
  16. return elID;
  17. }
  18. };
  19. var getCSS3Animate = function(obj, options) {
  20. var tmp, id, el = getEl(obj);
  21. //first one
  22. id = objId(el);
  23. if (cache[id]) {
  24. cache[id].animate(options);
  25. tmp = cache[id];
  26. } else {
  27. tmp = css3Animate(el, options);
  28. cache[id] = tmp;
  29. }
  30. return tmp;
  31. };
  32. $.fn["css3Animate"] = function(opts) {
  33. //keep old callback system - backwards compatibility - should be deprecated in future versions
  34. if (!opts.complete && opts.callback) opts.complete = opts.callback;
  35. //first on
  36. var tmp = getCSS3Animate(this[0], opts);
  37. opts.complete = null;
  38. opts.sucess = null;
  39. opts.failure = null;
  40. for (var i = 1; i < this.length; i++) {
  41. tmp.link(this[i], opts);
  42. }
  43. return tmp;
  44. };
  45. $["css3AnimateQueue"] = function() {
  46. return new css3Animate.queue();
  47. };
  48. var translateOpen = $.feat.cssTransformStart;
  49. var translateClose = $.feat.cssTransformEnd;
  50. var transitionEnd = $.feat.cssPrefix.replace(/-/g, "") + "TransitionEnd";
  51. transitionEnd = ($.os.fennec || $.feat.cssPrefix === "" || $.os.ie) ? "transitionend" : transitionEnd;
  52. transitionEnd = transitionEnd.replace(transitionEnd.charAt(0), transitionEnd.charAt(0).toLowerCase());
  53. var css3Animate = (function() {
  54. var css3Animate = function(elID, options) {
  55. if (!(this instanceof css3Animate)) return new css3Animate(elID, options);
  56. //start doing stuff
  57. this.callbacksStack = [];
  58. this.activeEvent = null;
  59. this.countStack = 0;
  60. this.isActive = false;
  61. this.el = elID;
  62. this.linkFinishedProxy_ = $.proxy(this.linkFinished, this);
  63. if (!this.el) return;
  64. this.animate(options);
  65. var that = this;
  66. af(this.el).bind('destroy', function() {
  67. var id = that.el.afCSS3AnimateId;
  68. that.callbacksStack = [];
  69. if (cache[id]) delete cache[id];
  70. });
  71. };
  72. css3Animate.prototype = {
  73. animate: function(options) {
  74. //cancel current active animation on this object
  75. if (this.isActive) this.cancel();
  76. this.isActive = true;
  77. if (!options) {
  78. alert("Please provide configuration options for animation of " + this.el.id);
  79. return;
  80. }
  81. var classMode = !! options["addClass"];
  82. var scale, time;
  83. var timeNum = numOnly(options["time"]);
  84. if (classMode) {
  85. //class defines properties being changed
  86. if (options["removeClass"]) {
  87. af(this.el).replaceClass(options["removeClass"], options["addClass"]);
  88. } else {
  89. af(this.el).addClass(options["addClass"]);
  90. }
  91. } else {
  92. //property by property
  93. if (timeNum === 0) options["time"] = 0;
  94. if (!options["y"]) options["y"] = 0;
  95. if (!options["x"]) options["x"] = 0;
  96. if (options["previous"]) {
  97. var cssMatrix = new $.getCssMatrix(this.el);
  98. options.y += numOnly(cssMatrix.f);
  99. options.x += numOnly(cssMatrix.e);
  100. }
  101. if (!options["origin"]) options.origin = "0% 0%";
  102. if (!options["scale"]) options.scale = "1";
  103. if (!options["rotateY"]) options.rotateY = "0";
  104. if (!options["rotateX"]) options.rotateX = "0";
  105. if (!options["skewY"]) options.skewY = "0";
  106. if (!options["skewX"]) options.skewX = "0";
  107. if (!options["timingFunction"]) options["timingFunction"] = "linear";
  108. //check for percent or numbers
  109. if (typeof(options.x) == "number" || (options.x.indexOf("%") == -1 && options.x.toLowerCase().indexOf("px") == -1 && options.x.toLowerCase().indexOf("deg") == -1)) options.x = parseInt(options.x, 10) + "px";
  110. if (typeof(options.y) == "number" || (options.y.indexOf("%") == -1 && options.y.toLowerCase().indexOf("px") == -1 && options.y.toLowerCase().indexOf("deg") == -1)) options.y = parseInt(options.y, 10) + "px";
  111. var trans = "translate" + translateOpen + (options.x) + "," + (options.y) + translateClose + " scale(" + parseFloat(options.scale) + ") rotate(" + options.rotateX + ")";
  112. if (!$.os.opera)
  113. trans += " rotateY(" + options.rotateY + ")";
  114. trans += " skew(" + options.skewX + "," + options.skewY + ")";
  115. this.el.style[$.feat.cssPrefix + "Transform"] = trans;
  116. this.el.style[$.feat.cssPrefix + "BackfaceVisibility"] = "hidden";
  117. var properties = $.feat.cssPrefix + "Transform";
  118. if (options["opacity"] !== undefined) {
  119. this.el.style.opacity = options["opacity"];
  120. properties += ", opacity";
  121. }
  122. if (options["width"]) {
  123. this.el.style.width = options["width"];
  124. properties = "all";
  125. }
  126. if (options["height"]) {
  127. this.el.style.height = options["height"];
  128. properties = "all";
  129. }
  130. this.el.style[$.feat.cssPrefix + "TransitionProperty"] = "all";
  131. if (("" + options["time"]).indexOf("s") === -1) {
  132. scale = 'ms';
  133. time = options["time"] + scale;
  134. } else if (options["time"].indexOf("ms") !== -1) {
  135. scale = 'ms';
  136. time = options["time"];
  137. } else {
  138. scale = 's';
  139. time = options["time"] + scale;
  140. }
  141. if (options.delay) {
  142. this.el.style[$.feat.cssPrefix + "TransitionDelay"] = options.delay;
  143. }
  144. this.el.style[$.feat.cssPrefix + "TransitionDuration"] = time;
  145. this.el.style[$.feat.cssPrefix + "TransitionTimingFunction"] = options["timingFunction"];
  146. this.el.style[$.feat.cssPrefix + "TransformOrigin"] = options.origin;
  147. }
  148. //add callback to the stack
  149. this.callbacksStack.push({
  150. complete: options["complete"],
  151. success: options["success"],
  152. failure: options["failure"]
  153. });
  154. this.countStack++;
  155. var that = this,
  156. duration;
  157. var style = window.getComputedStyle(this.el);
  158. if (classMode) {
  159. //get the duration
  160. duration = style[$.feat.cssPrefix + "TransitionDuration"];
  161. timeNum = numOnly(duration);
  162. options["time"] = timeNum;
  163. if (duration.indexOf("ms") !== -1) {
  164. scale = 'ms';
  165. } else {
  166. scale = 's';
  167. options["time"] *= 1000;
  168. }
  169. }
  170. //finish asap
  171. if (timeNum === 0 || (scale == 'ms' && timeNum < 5) || style.display == 'none') {
  172. //the duration is nearly 0 or the element is not displayed, finish immediatly
  173. $.asap($.proxy(this.finishAnimation, this, [false]));
  174. //this.finishAnimation();
  175. //set transitionend event
  176. } else {
  177. //setup the event normally
  178. this.activeEvent = function(event) {
  179. clearTimeout(that.timeout);
  180. that.finishAnimation(event);
  181. that.el.removeEventListener(transitionEnd, that.activeEvent, false);
  182. };
  183. that.timeout = setTimeout(this.activeEvent, numOnly(options["time"]) + 50);
  184. this.el.addEventListener(transitionEnd, this.activeEvent, false);
  185. }
  186. },
  187. addCallbackHook: function(callback) {
  188. if (callback) this.callbacksStack.push(callback);
  189. this.countStack++;
  190. return this.linkFinishedProxy_;
  191. },
  192. linkFinished: function(canceled) {
  193. if (canceled) this.cancel();
  194. else this.finishAnimation();
  195. },
  196. finishAnimation: function(event) {
  197. if (event && event.preventDefault) event.preventDefault();
  198. if (!this.isActive) return;
  199. this.countStack--;
  200. if (this.countStack === 0) this.fireCallbacks(false);
  201. },
  202. fireCallbacks: function(canceled) {
  203. this.clearEvents();
  204. //keep callbacks after cleanup
  205. // (if any of the callbacks overrides this object, callbacks will keep on fire as expected)
  206. var callbacks = this.callbacksStack;
  207. //cleanup
  208. this.cleanup();
  209. //fire all callbacks
  210. for (var i = 0; i < callbacks.length; i++) {
  211. var complete = callbacks[i]['complete'];
  212. var success = callbacks[i]['success'];
  213. var failure = callbacks[i]['failure'];
  214. //fire callbacks
  215. if (complete && typeof(complete) == "function") complete(canceled);
  216. //success/failure
  217. if (canceled && failure && typeof(failure) == "function") failure();
  218. else if (success && typeof(success) == "function") success();
  219. }
  220. },
  221. cancel: function() {
  222. if (!this.isActive) return;
  223. this.fireCallbacks(true); //fire failure callbacks
  224. },
  225. cleanup: function() {
  226. this.callbacksStack = [];
  227. this.isActive = false;
  228. this.countStack = 0;
  229. },
  230. clearEvents: function() {
  231. if (this.activeEvent) {
  232. this.el.removeEventListener(transitionEnd, this.activeEvent, false);
  233. }
  234. this.activeEvent = null;
  235. },
  236. link: function(elID, opts) {
  237. var callbacks = {
  238. complete: opts.complete,
  239. success: opts.success,
  240. failure: opts.failure
  241. };
  242. opts.complete = this.addCallbackHook(callbacks);
  243. opts.success = null;
  244. opts.failure = null;
  245. //run the animation with the replaced callbacks
  246. getCSS3Animate(elID, opts);
  247. //set the old callback back in the obj to avoid strange stuff
  248. opts.complete = callbacks.complete;
  249. opts.success = callbacks.success;
  250. opts.failure = callbacks.failure;
  251. return this;
  252. }
  253. };
  254. return css3Animate;
  255. })();
  256. css3Animate.queue = function() {
  257. return {
  258. elements: [],
  259. push: function(el) {
  260. this.elements.push(el);
  261. },
  262. pop: function() {
  263. return this.elements.pop();
  264. },
  265. run: function() {
  266. var that = this;
  267. if (this.elements.length === 0) return;
  268. if (typeof(this.elements[0]) == "function") {
  269. var func = this.shift();
  270. func();
  271. }
  272. if (this.elements.length === 0) return;
  273. var params = this.shift();
  274. if (this.elements.length > 0) params.complete = function(canceled) {
  275. if (!canceled) that.run();
  276. };
  277. css3Animate(document.getElementById(params.id), params);
  278. },
  279. shift: function() {
  280. return this.elements.shift();
  281. }
  282. };
  283. };
  284. })(af);