jquery fx 源码

  1. /*
  2. * author:prk date:2008-08-07 comment:analyse the fx of jQuery.
  3. *
  4. */
  5. jQuery.fn.extend({
  6. // show(speed,[callback])
  7. // 以优雅的动画隐藏所有匹配的元素,并在显示完成后可选地触发一个回调函数。
  8. // 可以根据指定的速度动态地改变每个匹配元素的高度、宽度和不透明度。
  9. // 显示隐藏的匹配元素 show()
  10. show: function(speed,callback){
  11. return speed ?
  12. this.animate({
  13. height: "show", width: "show", opacity: "show"
  14. }, speed, callback) :
  15. this.filter(":hidden").each(function(){
  16. this.style.display = this.oldblock || "";
  17. if ( jQuery.css(this,"display") == "none" ) {
  18. var elem = jQuery("<" + this.tagName + " />").appendTo("body");
  19. this.style.display = elem.css("display");// 默认的显示的display
  20. // handle an edge condition where css is - div {
  21. // display:none; } or similar
  22. if (this.style.display == "none")// 处理显式地设定了该tag不显示,只好采用b
  23. this.style.display = "block";
  24. elem.remove();// 上面这些的处理有没有必要呢?
  25. }
  26. }).end();// 回到前一个jQuery对象
  27. },
  28. // 与show相反
  29. hide: function(speed,callback){
  30. return speed ?
  31. this.animate({
  32. height: "hide", width: "hide", opacity: "hide"
  33. }, speed, callback) :
  34. this.filter(":visible").each(function(){
  35. this.oldblock = this.oldblock || jQuery.css(this,"display");
  36. this.style.display = "none";
  37. }).end();
  38. },
  39. // Save the old toggle function
  40. _toggle: jQuery.fn.toggle,
  41. // 切换元素的可见状态。
  42. // 如果元素是可见的,切换为隐藏的;如果元素是隐藏的,切换为可见的。
  43. // 每次点击后依次调用函数。
  44. // 如果点击了一个匹配的元素,则触发指定的第一个函数,当再次点击同一元素时,则触发指定的第二个函数,
  45. // 如果有更多函数,则再次触发,直到最后一个。随后的每次点击都重复对这几个函数的轮番调用。
  46. // 可以使用unbind("click")来删除。
  47. toggle: function( fn, fn2 ){
  48. return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
  49. this._toggle.apply( this, arguments ) :// 原来的toggle
  50. (fn ?
  51. this.animate({height: "toggle", width: "toggle", opacity: "toggle"}, fn, fn2)
  52. : this.each(function(){jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]();}
  53. // 对每个元素都调用show,或hide函数。
  54. )
  55. );
  56. },
  57. // 把所有匹配元素的不透明度以渐进方式调整到指定的不透明度,并在动画完成后可选地触发一个回调函数。
  58. // 这个动画只调整元素的不透明度,也就是说所有匹配的元素的高度和宽度不会发生变化。
  59. fadeTo: function(speed,to,callback){
  60. returnthis.animate({opacity: to}, speed, callback);
  61. },
  62. /**
  63. * 用于创建自定义动画的函数。
  64. * 这个函数的关键在于指定动画形式及结果样式属性对象。这个对象中每个属性都表示一个可以变化的样式属性(如“height”、“top”或“opacity”)。
  65. * 注意:所有指定的属性必须用骆驼形式,比如用marginLeft代替margin-left.
  66. * 而每个属性的值表示这个样式属性到多少时动画结束。如果是一个数值,样式属性就会从当前的值渐变到指定的值。如果使用的是“hide”、“show”或“toggle”这样的字符串值,则会为该属性调用默认的动画形式。
  67. * 在 jQuery 1.2 中,你可以使用 em 和 % 单位。另外,在 jQuery 1.2 中,你可以通过在属性值前面指定 "+=" 或
  68. * "-=" 来让元素做相对运动。
  69. *
  70. * params (Options) : 一组包含作为动画属性和终值的样式属性和及其值的集合 。 duration (String,Number)
  71. * :(可选) 三种预定速度之一的字符串("slow", "normal", or "fast")或表示动画时长的毫秒数值(如:1000)
  72. * easing (String) : (可选) 要使用的擦除效果的名称(需要插件支持).默认jQuery提供"linear" 和 "swing".
  73. * callback (Function) : (可选) 在动画完成时执行的函数
  74. */
  75. animate: function( prop, speed, easing, callback ) {
  76. var optall = jQuery.speed(speed, easing, callback);
  77. returnthis[ optall.queue === false ? "each" : "queue" ](function(){// 执行each或queue方法
  78. var opt = jQuery.extend({}, optall), p,
  79. hidden = this.nodeType == 1 && jQuery(this).is(":hidden"),// 是元素节点且是隐藏的
  80. self = this;// 当前的元素
  81. for ( p in prop ) {
  82. if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )// 已经是完成的状态
  83. return opt.complete.call(this);// 调用complate函数,在用户的callback加上队列的处理
  84. if ( ( p == "height" || p == "width" ) && this.style ) {// style中高度,宽度
  85. opt.display = jQuery.css(this, "display");// 保存当前元素的display
  86. opt.overflow = this.style.overflow;// 保证没有暗中进行的
  87. }
  88. }
  89. if ( opt.overflow != null )// 超出部分不见
  90. this.style.overflow = "hidden";
  91. opt.curAnim = jQuery.extend({}, prop);
  92. jQuery.each( prop, function(name, val){// 对当前元素的给定的属性进行变化的操作
  93. var e = new jQuery.fx( self, opt, name );
  94. if ( /toggle|show|hide/.test(val) )// 传参的属性可以用toggle,show,hide,其它
  95. // 调用当前e.show,e.hide,e.val的方法,jQuery.fx.prototype
  96. e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
  97. else {// 支持"+=" 或 "-=" 来让元素做相对运动。
  98. var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
  99. start = e.cur(true) || 0;// 当前的大小或0
  100. if ( parts ) {
  101. var end = parseFloat(parts[2]),// 值
  102. unit = parts[3] || "px";// 单位
  103. if ( unit != "px" ) {// 计算开始的值=(end/cur)*start
  104. self.style[ name ] = (end || 1) + unit;
  105. start = ((end || 1) / e.cur(true)) * start;
  106. self.style[ name ] = start + unit;
  107. }
  108. if ( parts[1] )// +=/-=,做相对运行
  109. end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
  110. e.custom( start, end, unit );// 动画
  111. } else
  112. e.custom( start, val, "" );// 动画
  113. }
  114. });
  115. // For JS strict compliance
  116. returntrue;
  117. });
  118. },
  119. // 实现队列操作,为jQuery对象中的每个元素都加type的属性,值为fn.
  120. queue: function(type, fn){
  121. // 可能看出支持一个fn的参数的形式,支持fn的array集形式
  122. if ( jQuery.isFunction(type) || ( type && type.constructor == Array )) {
  123. fn = type;
  124. type = "fx";
  125. }
  126. // type不存在,空字符等,无参数,type:string,fn不存在。肯定不是函数,也不是数组
  127. if ( !type || (typeof type == "string" && !fn) )
  128. return queue( this[0], type );
  129. returnthis.each(function(){
  130. if ( fn.constructor == Array )// 数组的形式
  131. queue(this, type, fn);// 存储在元素的type属性中
  132. else {
  133. queue(this, type).push( fn );// 加上一个
  134. if ( queue(this, type).length == 1 )
  135. fn.call(this);
  136. }
  137. });
  138. },
  139. stop: function(clearQueue, gotoEnd){
  140. var timers = jQuery.timers;
  141. if (clearQueue)
  142. this.queue([]);// 清除
  143. this.each(function(){
  144. // go in reverse order so anything added to the queue during the
  145. // loop is ignored
  146. for ( var i = timers.length - 1; i >= 0; i-- )
  147. if ( timers[i].elem == this ) {
  148. if (gotoEnd)
  149. // force the next step to be the last
  150. timers[i](true);
  151. timers.splice(i, 1);
  152. }
  153. });
  154. // start the next in the queue if the last step wasn't forced
  155. if (!gotoEnd)
  156. this.dequeue();
  157. returnthis;
  158. }
  159. });
  160. // Generate shortcuts for custom animations
  161. jQuery.each({
  162. slideDown: { height:"show" },
  163. slideUp: { height: "hide" },
  164. slideToggle: { height: "toggle" },
  165. fadeIn: { opacity: "show" },
  166. fadeOut: { opacity: "hide" }
  167. }, function( name, props ){
  168. jQuery.fn[ name ] = function( speed, callback ){
  169. returnthis.animate( props, speed, callback );
  170. };
  171. });
  172. // 为元素加上type的array的属性,或返回取到elem上type的值
  173. var queue = function( elem, type, array ) {
  174. if ( elem ){
  175. type = type || "fx";
  176. var q = jQuery.data( elem, type + "queue" );
  177. if ( !q || array )
  178. q = jQuery.data( elem, type + "queue", jQuery.makeArray(array) );
  179. }
  180. return q;
  181. };
  182. // 出列,根据type
  183. jQuery.fn.dequeue = function(type){
  184. type = type || "fx";
  185. returnthis.each(function(){
  186. var q = queue(this, type);// 得取type的的值
  187. q.shift();// 移出一个
  188. if ( q.length )
  189. q[0].call( this );
  190. });
  191. };
  192. jQuery.extend({
  193. // 主要用于辅助性的工作
  194. speed: function(speed, easing, fn) {
  195. var opt = speed && speed.constructor == Object ? speed : {// 采用紧凑型方式
  196. complete: fn || !fn && easing ||
  197. jQuery.isFunction( speed ) && speed,// coplete是至多三个参数的最后一个,当然是Fn.
  198. duration: speed,// 持继的时间
  199. easing: fn && easing || easing && easing.constructor != Function && easing// 不是Fn
  200. };
  201. opt.duration = (opt.duration && (opt.duration.constructor == Number ?
  202. opt.duration : jQuery.fx.speeds[opt.duration])) // 存在,不是数值,转找
  203. || jQuery.fx.speeds._default;// 默认的
  204. // Queueing
  205. opt.old = opt.complete;
  206. opt.complete = function(){// 排队的处理
  207. if ( opt.queue !== false )
  208. jQuery(this).dequeue();
  209. if ( jQuery.isFunction( opt.old ) )
  210. opt.old.call( this );
  211. };
  212. return opt;
  213. },
  214. // 擦除效果
  215. easing: {
  216. linear: function( p, n, firstNum, diff ) {
  217. return firstNum + diff * p;
  218. },
  219. swing: function( p, n, firstNum, diff ) {
  220. return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
  221. }
  222. },
  223. timers: [],// jQuery.timers
  224. timerId: null,
  225. // 根据参数构成一个对象
  226. fx: function( elem, options, prop ){
  227. this.options = options;
  228. this.elem = elem;
  229. this.prop = prop;
  230. if ( !options.orig )
  231. options.orig = {};
  232. }
  233. });
  234. jQuery.fx.prototype = {
  235. // 为元素设值,更新显示
  236. update: function(){
  237. if ( this.options.step )
  238. this.options.step.call( this.elem, this.now, this );
  239. (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );// 为元素的设值,fx.prop
  240. // 对于高度和宽度,采用display=block的形式。
  241. if ( ( this.prop == "height" || this.prop == "width" ) && this.elem.style )
  242. this.elem.style.display = "block";
  243. },
  244. // 当前元素当前属性的值
  245. cur: function(force){
  246. if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) )
  247. returnthis.elem[ this.prop ];
  248. var r = parseFloat(jQuery.css(this.elem, this.prop, force));
  249. return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
  250. },
  251. // 开动一个动画
  252. custom: function(from, to, unit){
  253. this.startTime = now();
  254. this.start = from;
  255. this.end = to;
  256. this.unit = unit || this.unit || "px";
  257. this.now = this.start;
  258. this.pos = this.state = 0;
  259. this.update();
  260. var self = this;
  261. function t(gotoEnd){
  262. return self.step(gotoEnd);// 调用step(gotoEnd)//本对象的
  263. }
  264. t.elem = this.elem;
  265. jQuery.timers.push(t);
  266. // 第一次,或上一次已经把jQuery.timers全部执行完了。
  267. if ( jQuery.timerId == null ) {
  268. jQuery.timerId = setInterval(function(){
  269. var timers = jQuery.timers;
  270. for ( var i = 0; i < timers.length; i++ )// 执行timers中所有
  271. if ( !timers[i]() )// 执行了一次,就从timers中除去
  272. timers.splice(i--, 1);
  273. if ( !timers.length ) {// length==0,取消Interval
  274. clearInterval( jQuery.timerId );
  275. jQuery.timerId = null;
  276. }
  277. }, 13);
  278. }
  279. },
  280. // Simple 'show' function
  281. show: function(){
  282. // 保存当前的,以被修改之后能得到初始的值
  283. this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
  284. this.options.show = true;
  285. this.custom(0, this.cur());
  286. // Make sure that we start at a small width/height to avoid any
  287. // flash of content
  288. if ( this.prop == "width" || this.prop == "height" )
  289. this.elem.style[this.prop] = "1px";
  290. // 开始显示元素
  291. jQuery(this.elem).show();
  292. },
  293. // 隐藏
  294. hide: function(){
  295. // 保存当前的,以被修改之后能得到初始的值
  296. this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
  297. this.options.hide = true;
  298. this.custom(this.cur(), 0);
  299. },
  300. // 动画的每一个步骤
  301. step: function(gotoEnd){
  302. var t = now();
  303. // 结束或当前时间大于startTime+duration
  304. if ( gotoEnd || t > this.options.duration + this.startTime ) {
  305. this.now = this.end;
  306. this.pos = this.state = 1;
  307. this.update();
  308. this.options.curAnim[ this.prop ] = true;
  309. var done = true;
  310. for ( var i in this.options.curAnim )
  311. if ( this.options.curAnim[i] !== true )
  312. done = false;
  313. if ( done ) {// 重置所有的设定
  314. if ( this.options.display != null ) {// Reset the overflow
  315. this.elem.style.overflow = this.options.overflow;
  316. // Reset the display
  317. this.elem.style.display = this.options.display;
  318. if ( jQuery.css(this.elem, "display") == "none" )
  319. this.elem.style.display = "block";
  320. }
  321. // Hide the element if the "hide" operation was done
  322. if ( this.options.hide )
  323. this.elem.style.display = "none";
  324. // Reset the properties, if the item has been hidden or shown
  325. if ( this.options.hide || this.options.show )
  326. for ( var p in this.options.curAnim )
  327. jQuery.attr(this.elem.style, p, this.options.orig[p]);
  328. }
  329. if ( done )// 运行complete的回调函数
  330. this.options.complete.call( this.elem );
  331. returnfalse;
  332. } else {
  333. var n = t - this.startTime;
  334. this.state = n / this.options.duration;
  335. // Perform the easing function, defaults to swing
  336. this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
  337. this.now = this.start + ((this.end - this.start) * this.pos);
  338. // Perform the next step of the animation
  339. this.update();
  340. }
  341. returntrue;
  342. }
  343. };
  344. jQuery.extend( jQuery.fx, {
  345. // 动画的速度
  346. speeds:{
  347. slow: 600,
  348. fast: 200,
  349. // Default speed
  350. _default: 400
  351. },
  352. // 为元素设值
  353. step: {
  354. opacity: function(fx){// 为元素CSS设opacity为fx
  355. jQuery.attr(fx.elem.style, "opacity", fx.now);
  356. },
  357. _default: function(fx){
  358. if( fx.prop in fx.elem ) // 对于元素
  359. fx.elem[ fx.prop ] = fx.now;
  360. elseif( fx.elem.style )// 对于style
  361. fx.elem.style[ fx.prop ] = fx.now + fx.unit;
  362. }
  363. }
  364. });