zoukankan      html  css  js  c++  java
  • jQuery1.9.1源码分析--Animation模块

      1 var fxNow,
      2         // 使用一个ID来执行动画setInterval 
      3         timerId,
      4         rfxtypes = /^(?:toggle|show|hide)$/,
      5         // eg: +=30.5px
      6         // 执行exec匹配["+=30.5px", "+", "30.5", "px"]
      7         rfxnum = new RegExp('^(?:([+-])=|)(' + core_pnum + ')([a-z%]*)$', 'i'),
      8         // 以“queueHooks”结尾
      9         rrun = /queueHooks$/,
     10         animationPrefilters = [defaultPrefilter],
     11         tweeners = {
     12             // 在动画前再次对动画参数做调整
     13             '*': [
     14                 function(prop, value) {
     15                     var end, unit,
     16                         // this指向animation对象
     17                         // 返回一个Tween构造函数实例
     18                         tween = this.createTween(prop, value),
     19                         // eg:["+=30.5px", "+", "30.5", "px"]
     20                         parts = rfxnum.exec(value),
     21                         // 计算当前属性样式值
     22                         target = tween.cur(),
     23                         start = +target || 0,
     24                         scale = 1,
     25                         maxIterations = 20;
     26 
     27                     if (parts) {
     28                         // 数值
     29                         end = +parts[2];
     30                         // 单位
     31                         // jQuery.cssNumber里面的值是不需要单位的
     32                         unit = parts[3] || (jQuery.cssNumber[prop] ? '' : 'px');
     33 
     34                         // We need to compute starting value
     35                         // 我们需要计算开始值
     36                         if (unit !== 'px' && start) {
     37                             // Iteratively approximate from a nonzero starting point
     38                             // Prefer the current property, because this process will be trivial if it uses the same units
     39                             // Fallback to end or a simple constant
     40                             // 尝试从元素样式中获取开始值
     41                             start = jQuery.css(tween.elem, prop, true) || end || 1;
     42 
     43                             do {
     44                                 // If previos iteration zeroed out, double until we get *something*
     45                                 // Use a string for doubling factor so we don't accidentally see scale as unchanged below
     46                                 scale = scale || '.5';
     47 
     48                                 // Adjust and apply
     49                                 start = start / scale;
     50                                 jQuery.style(tween.elem, prop, start + unit);
     51 
     52                                 // Update scale, tolerating zero or NaN from tween.cur()
     53                                 // And breaking the loop if scale is unchanged or perfect. or if we've just had enough
     54                             } while (scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations);
     55                         }
     56 
     57                         tween.unit = unit;
     58                         tween.start = start;
     59                         // If a +=/-= token was provided, we're doing a relative animation
     60                         tween.end = parts[1] ? start + (parts[1] + 1) * end : end;
     61                     }
     62                     return tween;
     63                 }
     64             ]
     65         };
     66 
     67     // Animations created synchronous will run synchronously
     68     // TODO
     69     // 返回一个时间戳,然后用setTimeout延时将fxNow设置为undefined
     70 
     71     function createFxNow() {
     72         setTimeout(function() {
     73             fxNow = undefined;
     74         });
     75         return (fxNow = jQuery.now());
     76     }
     77 
     78     function createTweens(animation, props) {
     79         // 遍历props动画属性对象,并执行回调
     80         jQuery.each(props, function(prop, value) {
     81             // 如果tweeners[prop]数组存在,将它和tweeners['*']连接
     82             var collection = (tweeners[prop] || []).concat(tweeners['*']),
     83                 index = 0,
     84                 length = collection.length;
     85 
     86             // 遍历函数数组
     87             for (; index < length; index++) {
     88                 // 如果该函数有返回值,且==true,退出函数
     89                 if (collection[index].call(animation, prop, value)) {
     90                     // We're done with this property
     91                     return;
     92                 }
     93             }
     94         });
     95     }
     96 
     97     function Animation(elem, properties, options) {
     98         var result, stopped, index = 0,
     99             length = animationPrefilters.length,
    100             // deferred无论成功还是失败都会删除elem元素
    101             deferred = jQuery.Deferred().always(function() {
    102                 // don't match elem in the :animated selector
    103                 // 在“:animated”选择器中不会匹配到它们
    104                 delete tick.elem;
    105             }),
    106             tick = function() {
    107                 if (stopped) {
    108                     return false;
    109                 }
    110                 var // 计算当前动画时间戳
    111                     currentTime = fxNow || createFxNow(),
    112                     // 结束时间减当前时间,计算出剩余时间
    113                     remaining = Math.max(0, animation.startTime + animation.duration - currentTime),
    114                     // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
    115                     // 剩余时间百分比
    116                     temp = remaining / animation.duration || 0,
    117                     // 已执行百分比
    118                     percent = 1 - temp,
    119                     index = 0,
    120                     // 动画属性对应的tweens
    121                     length = animation.tweens.length;
    122 
    123                 // 遍历tweens,并执行对应的run方法,将已执行百分比通过传参传入
    124                 // run方法通过缓动算法计算出样式值,然后应用到元素上
    125                 for (; index < length; index++) {
    126                     animation.tweens[index].run(percent);
    127                 }
    128 
    129                 // 触发notify回调列表
    130                 deferred.notifyWith(elem, [animation, percent, remaining]);
    131 
    132                 // 如果执行进度为完成且tweens数组有元素
    133                 // 返回剩余时间
    134                 if (percent < 1 && length) {
    135                     return remaining;
    136                 } else {
    137                     // 否则表示已完成,触发resolve回调列表,
    138                     // 并返回false值
    139                     deferred.resolveWith(elem, [animation]);
    140                     return false;
    141                 }
    142             },
    143             animation = deferred.promise({
    144                 // 动画元素
    145                 elem: elem,
    146                 // 需要动画的属性
    147                 props: jQuery.extend({}, properties),
    148                 // 给optall添加specialEasing属性对象
    149                 opts: jQuery.extend(true, {
    150                     specialEasing: {}
    151                 }, options),
    152                 // 原始动画属性
    153                 originalProperties: properties,
    154                 // 原始的配置项optall
    155                 originalOptions: options,
    156                 // 动画开始时间,使用当前时间的毫秒数
    157                 startTime: fxNow || createFxNow(),
    158                 // 动画时长
    159                 duration: options.duration,
    160                 tweens: [],
    161                 createTween: function(prop, end) {
    162                     var tween = jQuery.Tween(elem, animation.opts, prop, end, animation.opts.specialEasing[prop] || animation.opts.easing);
    163                     animation.tweens.push(tween);
    164                     return tween;
    165                 },
    166                 stop: function(gotoEnd) {
    167                     var index = 0,
    168                         // if we are going to the end, we want to run all the tweens
    169                         // otherwise we skip this part
    170                         length = gotoEnd ? animation.tweens.length : 0;
    171                     if (stopped) {
    172                         return this;
    173                     }
    174                     stopped = true;
    175                     for (; index < length; index++) {
    176                         animation.tweens[index].run(1);
    177                     }
    178 
    179                     // resolve when we played the last frame
    180                     // otherwise, reject
    181                     if (gotoEnd) {
    182                         deferred.resolveWith(elem, [animation, gotoEnd]);
    183                     } else {
    184                         deferred.rejectWith(elem, [animation, gotoEnd]);
    185                     }
    186                     return this;
    187                 }
    188             }),
    189             props = animation.props;
    190 
    191         /*
    192         将是动画属性转换成驼峰式,并设置其相应的缓动属性,
    193         如果存在cssHooks钩子对象,则需要另作一番处理
    194          */
    195         propFilter(props, animation.opts.specialEasing);
    196 
    197         // 遍历动画预过滤器,并执行回调
    198         // 其中defaultPrefilter为默认预过滤器,每次都会执行
    199         for (; index < length; index++) {
    200             result = animationPrefilters[index].call(animation, elem, props, animation.opts);
    201             // 如果有返回值,退出函数
    202             if (result) {
    203                 return result;
    204             }
    205         }
    206 
    207         createTweens(animation, props);
    208 
    209         if (jQuery.isFunction(animation.opts.start)) {
    210             animation.opts.start.call(elem, animation);
    211         }
    212 
    213         // 开始执行动画
    214         jQuery.fx.timer(
    215             jQuery.extend(tick, {
    216                 elem: elem,
    217                 anim: animation,
    218                 queue: animation.opts.queue
    219             }));
    220 
    221         // attach callbacks from options
    222         return animation.progress(animation.opts.progress).done(animation.opts.done, animation.opts.complete).fail(animation.opts.fail).always(animation.opts.always);
    223     }
    224 
    225     /**
    226      * 动画属性调整与过滤
    227      * 
    228      * 将是动画属性转换成驼峰式,并设置其相应的缓动属性,
    229      * 如果存在cssHooks钩子对象,则需要另作一番处理
    230      * @param  {[type]} props         [需要动画的属性]
    231      * @param  {[type]} specialEasing [description]
    232      * @return {[type]}               [description]
    233      */
    234     function propFilter(props, specialEasing) {
    235         var value, name, index, easing, hooks;
    236 
    237         // camelCase, specialEasing and expand cssHook pass
    238         for (index in props) {
    239             // 驼峰化属性
    240             name = jQuery.camelCase(index);
    241             // TODO
    242             easing = specialEasing[name];
    243             // 属性值
    244             value = props[index];
    245             // 如果属性值是数组
    246             if (jQuery.isArray(value)) {
    247                 easing = value[1];
    248                 // 取数组第一个元素为属性值
    249                 value = props[index] = value[0];
    250             }
    251 
    252             // 如果属性名精过驼峰化后,删除原有的属性名,减少占用内存
    253             if (index !== name) {
    254                 props[name] = value;
    255                 delete props[index];
    256             }
    257 
    258             // 处理兼容性的钩子对象
    259             hooks = jQuery.cssHooks[name];
    260             // 如果存在钩子对象且有expand属性
    261             if (hooks && "expand" in hooks) {
    262                 // 返回expand处理后的value值
    263                 // 该类型是一个对象,属性是
    264                 // (margin|padding|borderWidth)(Top|Right|Bottom|Left)
    265                 value = hooks.expand(value);
    266 
    267                 // 我们已经不需要name属性了
    268                 delete props[name];
    269 
    270                 // not quite $.extend, this wont overwrite keys already present.
    271                 // also - reusing 'index' from above because we have the correct "name"
    272                 for (index in value) {
    273                     // 如果props没有(margin|padding|borderWidth)(Top|Right|Bottom|Left)属性
    274                     // 添加该属性和对应的值,并设置缓动属性
    275                     if (!(index in props)) {
    276                         props[index] = value[index];
    277                         specialEasing[index] = easing;
    278                     }
    279                 }
    280             } else {
    281                 // 没有钩子对象就直接设置其为缓动属性
    282                 specialEasing[name] = easing;
    283             }
    284         }
    285     }
    286 
    287     jQuery.Animation = jQuery.extend(Animation, {
    288 
    289         tweener: function(props, callback) {
    290             if (jQuery.isFunction(props)) {
    291                 callback = props;
    292                 props = ["*"];
    293             } else {
    294                 props = props.split(" ");
    295             }
    296 
    297             var prop, index = 0,
    298                 length = props.length;
    299 
    300             for (; index < length; index++) {
    301                 prop = props[index];
    302                 tweeners[prop] = tweeners[prop] || [];
    303                 tweeners[prop].unshift(callback);
    304             }
    305         },
    306         // 为animationPrefilters回调数组添加回调
    307         prefilter: function(callback, prepend) {
    308             if (prepend) {
    309                 animationPrefilters.unshift(callback);
    310             } else {
    311                 animationPrefilters.push(callback);
    312             }
    313         }
    314     });
    315 
    316     /**
    317      * 动画预处理
    318      * 添加fx队列缓存(没有的话),对动画属性“width/height,overflow”, 值有“toggle/show/hide”采取的一些措施
    319      * 
    320      * @param  {[type]} elem  [动画元素]
    321      * @param  {[type]} props [动画属性]
    322      * @param  {[type]} opts  [动画配置项]
    323      * @return {[type]}       [description]
    324      */
    325     function defaultPrefilter(elem, props, opts) { /*jshint validthis:true */
    326         var prop, index, length, value, dataShow, toggle, tween, hooks, oldfire,
    327             // animation对象(同时是个deferred对象)
    328             anim = this,
    329             style = elem.style,
    330             orig = {},
    331             handled = [],
    332             hidden = elem.nodeType && isHidden(elem);
    333 
    334         // handle queue: false promises
    335         if (!opts.queue) {
    336             // 获取或者设置动画队列钩子
    337             hooks = jQuery._queueHooks(elem, "fx");
    338             // 如果hooks.unqueued为null/undefined
    339             if (hooks.unqueued == null) {
    340                 hooks.unqueued = 0;
    341                 // 获取旧的empty回调对象
    342                 // 用于清除动画队列缓存
    343                 oldfire = hooks.empty.fire;
    344                 // 装饰,添加新的职责
    345                 hooks.empty.fire = function() {
    346                     // 当hooks.unqueued为0时执行清除动画队列缓存
    347                     if (!hooks.unqueued) {
    348                         oldfire();
    349                     }
    350                 };
    351             }
    352             hooks.unqueued++;
    353 
    354             anim.always(function() {
    355                 // doing this makes sure that the complete handler will be called
    356                 // before this completes
    357                 // 延迟处理,确保该回调完成才调用下面回调
    358                 anim.always(function() {
    359                     hooks.unqueued--;
    360                     // 如果动画队列没有元素了,清空缓存
    361                     if (!jQuery.queue(elem, "fx").length) {
    362                         hooks.empty.fire();
    363                     }
    364                 });
    365             });
    366         }
    367 
    368         // height/width overflow pass
    369         // 对width或height的DOM元素的动画前的处理
    370         if (elem.nodeType === 1 && ("height" in props || "width" in props)) {
    371             // Make sure that nothing sneaks out
    372             // Record all 3 overflow attributes because IE does not
    373             // change the overflow attribute when overflowX and
    374             // overflowY are set to the same value
    375             // IE不会改变overflow属性当iverflowX和overflowY的值相同时。
    376             // 因此我们要记录三个overflow的属性
    377             opts.overflow = [style.overflow, style.overflowX, style.overflowY];
    378 
    379             // Set display property to inline-block for height/width
    380             // animations on inline elements that are having width/height animated
    381             // 将inline元素(非浮动的)设置为inline-block或者BFC(iE6/7),使它们的width和height可改变
    382             if (jQuery.css(elem, "display") === "inline" && jQuery.css(elem, "float") === "none") {
    383 
    384                 // inline-level elements accept inline-block;
    385                 // block-level elements need to be inline with layout
    386                 if (!jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay(elem.nodeName) === "inline") {
    387                     style.display = "inline-block";
    388 
    389                 } else {
    390                     style.zoom = 1;
    391                 }
    392             }
    393         }
    394 
    395         if (opts.overflow) {
    396             style.overflow = "hidden";
    397             // 如果不支持父元素随着子元素宽度改变而改变
    398             // 动画结束后将style设置为初始状态
    399             if (!jQuery.support.shrinkWrapBlocks) {
    400                 anim.always(function() {
    401                     style.overflow = opts.overflow[0];
    402                     style.overflowX = opts.overflow[1];
    403                     style.overflowY = opts.overflow[2];
    404                 });
    405             }
    406         }
    407 
    408 
    409         // show/hide pass
    410         // 遍历动画属性
    411         for (index in props) {
    412             // 获取目标值
    413             value = props[index];
    414             // 判断值是否有toggle|show|hide
    415             if (rfxtypes.exec(value)) {
    416                 delete props[index];
    417                 // 是否需要toggle
    418                 toggle = toggle || value === "toggle";
    419                 // 如果hide(或者show)状态的初始值和我们动画的值相同,就不需要做处理
    420                 if (value === (hidden ? "hide" : "show")) {
    421                     continue;
    422                 }
    423                 // 将需要show/hide/toggle的属性保存到handled数组中
    424                 handled.push(index);
    425             }
    426         }
    427 
    428         length = handled.length;
    429         // 如果handled数组有元素
    430         // 对需要toggle|show|hide的属性处理
    431         if (length) {
    432             // 获取或者设置元素的fxshow缓存(保存显示状态)
    433             dataShow = jQuery._data(elem, "fxshow") || jQuery._data(elem, "fxshow", {});
    434             // 如果元素已经有hidden属性,说明我们设置过了,
    435             // 取该值
    436             if ("hidden" in dataShow) {
    437                 hidden = dataShow.hidden;
    438             }
    439 
    440             // store state if its toggle - enables .stop().toggle() to "reverse"
    441             // 如果需要toggle,将hidden状态取反
    442             if (toggle) {
    443                 dataShow.hidden = !hidden;
    444             }
    445             // 如果元素隐藏了就显示出来,为了后期的动画
    446             if (hidden) {
    447                 jQuery(elem).show();
    448             } else {
    449                 // 否则动画结束后才隐藏
    450                 anim.done(function() {
    451                     jQuery(elem).hide();
    452                 });
    453             }
    454             // 动画结束后删除fxshow缓存,并恢复元素原始样式
    455             anim.done(function() {
    456                 var prop;
    457                 jQuery._removeData(elem, "fxshow");
    458                 for (prop in orig) {
    459                     jQuery.style(elem, prop, orig[prop]);
    460                 }
    461             });
    462             for (index = 0; index < length; index++) {
    463                 prop = handled[index];
    464                 // 创建Tween实例
    465                 tween = anim.createTween(prop, hidden ? dataShow[prop] : 0);
    466                 // 获取元素原始样式值
    467                 orig[prop] = dataShow[prop] || jQuery.style(elem, prop);
    468 
    469                 // 如果dataShow引用的缓存没有show|hide|toggle属性
    470                 if (!(prop in dataShow)) {
    471                     // 添加该属性,并赋初值
    472                     dataShow[prop] = tween.start;
    473                     if (hidden) {
    474                         tween.end = tween.start;
    475                         tween.start = prop === "width" || prop === "height" ? 1 : 0;
    476                     }
    477                 }
    478             }
    479         }
    480     }
    481 
    482     // 实例化init构造函数
    483     // 对单个动画属性,在初始化的时候计算开始值
    484     function Tween(elem, options, prop, end, easing) {
    485         return new Tween.prototype.init(elem, options, prop, end, easing);
    486     }
    487     jQuery.Tween = Tween;
    488 
    489     Tween.prototype = {
    490         constructor: Tween,
    491         init: function(elem, options, prop, end, easing, unit) {
    492             this.elem = elem;
    493             this.prop = prop;
    494             this.easing = easing || "swing";
    495             this.options = options;
    496             this.start = this.now = this.cur();
    497             this.end = end;
    498             this.unit = unit || (jQuery.cssNumber[prop] ? "" : "px");
    499         },
    500         cur: function() {
    501             var hooks = Tween.propHooks[this.prop];
    502 
    503             return hooks && hooks.get ? hooks.get(this) : Tween.propHooks._default.get(this);
    504         },
    505         // 通过缓动算法计算出样式值,然后应用到元素上
    506         run: function(percent) {
    507             var eased, hooks = Tween.propHooks[this.prop];
    508 
    509             // 当前执行位置,
    510             // 如果有时长,就用缓动算法
    511             if (this.options.duration) {
    512                 this.pos = eased = jQuery.easing[this.easing](
    513                     percent, this.options.duration * percent, 0, 1, this.options.duration);
    514             } else {
    515                 this.pos = eased = percent;
    516             }
    517             // 当前时间戳
    518             this.now = (this.end - this.start) * eased + this.start;
    519 
    520             if (this.options.step) {
    521                 this.options.step.call(this.elem, this.now, this);
    522             }
    523 
    524             // 有钩子对象就执行set方法,否则使用默认set方法
    525             if (hooks && hooks.set) {
    526                 hooks.set(this);
    527             } else {
    528                 Tween.propHooks._default.set(this);
    529             }
    530             return this;
    531         }
    532     };
    533 
    534     Tween.prototype.init.prototype = Tween.prototype;
    535 
    536     Tween.propHooks = {
    537         _default: {
    538             // 默认的获取样式初始值方法
    539             get: function(tween) {
    540                 var result;
    541 
    542                 if (tween.elem[tween.prop] != null && (!tween.elem.style || tween.elem.style[tween.prop] == null)) {
    543                     return tween.elem[tween.prop];
    544                 }
    545 
    546                 // passing an empty string as a 3rd parameter to .css will automatically
    547                 // attempt a parseFloat and fallback to a string if the parse fails
    548                 // so, simple values such as "10px" are parsed to Float.
    549                 // complex values such as "rotate(1rad)" are returned as is.
    550                 result = jQuery.css(tween.elem, tween.prop, "");
    551                 // Empty strings, null, undefined and "auto" are converted to 0.
    552                 return !result || result === "auto" ? 0 : result;
    553             },
    554             // 设置元素样式
    555             set: function(tween) {
    556                 // use step hook for back compat - use cssHook if its there - use .style if its
    557                 // available and use plain properties where available
    558                 if (jQuery.fx.step[tween.prop]) {
    559                     jQuery.fx.step[tween.prop](tween);
    560                 } else if (tween.elem.style && (tween.elem.style[jQuery.cssProps[tween.prop]] != null || jQuery.cssHooks[tween.prop])) {
    561                     jQuery.style(tween.elem, tween.prop, tween.now + tween.unit);
    562                 } else {
    563                     tween.elem[tween.prop] = tween.now;
    564                 }
    565             }
    566         }
    567     };
    568 
    569     // Remove in 2.0 - this supports IE8's panic based approach
    570     // to setting things on disconnected nodes
    571     Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
    572         set: function(tween) {
    573             if (tween.elem.nodeType && tween.elem.parentNode) {
    574                 tween.elem[tween.prop] = tween.now;
    575             }
    576         }
    577     };
    578 
    579     jQuery.each(["toggle", "show", "hide"], function(i, name) {
    580         var cssFn = jQuery.fn[name];
    581         jQuery.fn[name] = function(speed, easing, callback) {
    582             return speed == null || typeof speed === "boolean" ? cssFn.apply(this, arguments) : this.animate(genFx(name, true), speed, easing, callback);
    583         };
    584     });
    585 
    586     jQuery.fn.extend({
    587         fadeTo: function(speed, to, easing, callback) {
    588 
    589             // show any hidden elements after setting opacity to 0
    590             return this.filter(isHidden).css("opacity", 0).show()
    591 
    592             // animate to the value specified
    593             .end().animate({
    594                 opacity: to
    595             }, speed, easing, callback);
    596         },
    597         animate: function(prop, speed, easing, callback) {
    598             var // prop对象是否为空
    599                 empty = jQuery.isEmptyObject(prop),
    600                 // 返回{complete, duration, easing, queue, old}
    601                 optall = jQuery.speed(speed, easing, callback),
    602                 // TODO
    603                 doAnimation = function() {
    604                     // Operate on a copy of prop so per-property easing won't be lost
    605                     var anim = Animation(this, jQuery.extend({}, prop), optall);
    606                     doAnimation.finish = function() {
    607                         anim.stop(true);
    608                     };
    609                     // Empty animations, or finishing resolves immediately
    610                     if (empty || jQuery._data(this, "finish")) {
    611                         anim.stop(true);
    612                     }
    613                 };
    614             doAnimation.finish = doAnimation;
    615 
    616                 // 如果prop为空对象或者queue为false(即不进行动画队列),
    617                 // 遍历元素集并执行doAnimation回调
    618             return empty || optall.queue === false ? this.each(doAnimation) :
    619                 // 否则prop不为空且需要队列执行,
    620                 // 将doAnimation添加到该元素的队列中
    621                 // jQuery.queue('fx', doAnimation)
    622                 this.queue(optall.queue, doAnimation);
    623         },
    624         // 停止所有在指定元素上正在运行的动画。
    625         stop: function(type, clearQueue, gotoEnd) {
    626             var stopQueue = function(hooks) {
    627                 var stop = hooks.stop;
    628                 delete hooks.stop;
    629                 stop(gotoEnd);
    630             };
    631 
    632             if (typeof type !== "string") {
    633                 gotoEnd = clearQueue;
    634                 clearQueue = type;
    635                 type = undefined;
    636             }
    637             if (clearQueue && type !== false) {
    638                 this.queue(type || "fx", []);
    639             }
    640 
    641             return this.each(function() {
    642                 var dequeue = true,
    643                     index = type != null && type + "queueHooks",
    644                     timers = jQuery.timers,
    645                     data = jQuery._data(this);
    646 
    647                 if (index) {
    648                     if (data[index] && data[index].stop) {
    649                         stopQueue(data[index]);
    650                     }
    651                 } else {
    652                     for (index in data) {
    653                         if (data[index] && data[index].stop && rrun.test(index)) {
    654                             stopQueue(data[index]);
    655                         }
    656                     }
    657                 }
    658 
    659                 for (index = timers.length; index--;) {
    660                     if (timers[index].elem === this && (type == null || timers[index].queue === type)) {
    661                         timers[index].anim.stop(gotoEnd);
    662                         dequeue = false;
    663                         timers.splice(index, 1);
    664                     }
    665                 }
    666 
    667                 // start the next in the queue if the last step wasn't forced
    668                 // timers currently will call their complete callbacks, which will dequeue
    669                 // but only if they were gotoEnd
    670                 if (dequeue || !gotoEnd) {
    671                     jQuery.dequeue(this, type);
    672                 }
    673             });
    674         },
    675         finish: function(type) {
    676             if (type !== false) {
    677                 type = type || "fx";
    678             }
    679             return this.each(function() {
    680                 var index, data = jQuery._data(this),
    681                     queue = data[type + "queue"],
    682                     hooks = data[type + "queueHooks"],
    683                     timers = jQuery.timers,
    684                     length = queue ? queue.length : 0;
    685 
    686                 // enable finishing flag on private data
    687                 data.finish = true;
    688 
    689                 // empty the queue first
    690                 jQuery.queue(this, type, []);
    691 
    692                 if (hooks && hooks.cur && hooks.cur.finish) {
    693                     hooks.cur.finish.call(this);
    694                 }
    695 
    696                 // look for any active animations, and finish them
    697                 for (index = timers.length; index--;) {
    698                     if (timers[index].elem === this && timers[index].queue === type) {
    699                         timers[index].anim.stop(true);
    700                         timers.splice(index, 1);
    701                     }
    702                 }
    703 
    704                 // look for any animations in the old queue and finish them
    705                 for (index = 0; index < length; index++) {
    706                     if (queue[index] && queue[index].finish) {
    707                         queue[index].finish.call(this);
    708                     }
    709                 }
    710 
    711                 // turn off finishing flag
    712                 delete data.finish;
    713             });
    714         }
    715     });
    716 
    717     // Generate parameters to create a standard animation
    718     /**
    719      * 用于填充slideDown/slideUp/slideToggle动画参数
    720      * @param  {[String]} type         [show/hide/toggle]
    721      * @param  {[type]} includeWidth [是否需要包含宽度]
    722      * @return {[type]}              [description]
    723      */
    724     function genFx(type, includeWidth) {
    725         var which,
    726             attrs = {
    727                 height: type
    728             },
    729             i = 0;
    730 
    731         // if we include width, step value is 1 to do all cssExpand values,
    732         // if we don't include width, step value is 2 to skip over Left and Right
    733         includeWidth = includeWidth ? 1 : 0;
    734         // 不包含宽度,which就取“Top/Bottom”,
    735         // 否则“Left/Right”
    736         for (; i < 4; i += 2 - includeWidth) {
    737             which = cssExpand[i];
    738             attrs["margin" + which] = attrs["padding" + which] = type;
    739         }
    740 
    741         if (includeWidth) {
    742             attrs.opacity = attrs.width = type;
    743         }
    744 
    745         return attrs;
    746     }
    747 
    748     // Generate shortcuts for custom animations
    749     jQuery.each({
    750         slideDown: genFx("show"),
    751         slideUp: genFx("hide"),
    752         slideToggle: genFx("toggle"),
    753         fadeIn: {
    754             opacity: "show"
    755         },
    756         fadeOut: {
    757             opacity: "hide"
    758         },
    759         fadeToggle: {
    760             opacity: "toggle"
    761         }
    762     }, function(name, props) {
    763         jQuery.fn[name] = function(speed, easing, callback) {
    764             return this.animate(props, speed, easing, callback);
    765         };
    766     });
    767 
    768     /**
    769      * 配置动画参数
    770      * 
    771      * 配置动画时长,动画结束回调(经装饰了),缓动算法,queue属性用来标识是动画队列
    772      * @param  {[Number|Objecct]}   speed  [动画时长]
    773      * @param  {[Function]}   easing [缓动算法]
    774      * @param  {Function} fn     [动画结束会掉]
    775      * @return {[Object]}          [description]
    776      */
    777     jQuery.speed = function(speed, easing, fn) {
    778         var opt =
    779             // speed是否为对象
    780             speed && typeof speed === "object" ?
    781             // 如果是,克隆speed对象
    782             jQuery.extend({}, speed) :
    783             // 否则返回一个新的对象
    784             {
    785                 // complete是我们的animate的回调方法,
    786                 // 即动画结束时的回调
    787                 // (speed, easing, fn)
    788                 // (speed || easing, fn)
    789                 // (fn)
    790                 complete: fn || !fn && easing || jQuery.isFunction(speed) && speed,
    791                 // 动画时长
    792                 duration: speed,
    793                 // 缓动
    794                 easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
    795             };
    796 
    797         opt.duration =
    798             // jQuery.fx.off是否为真,如果是则将opt.duration设置为0,
    799             // 这将会停止所有动画
    800             jQuery.fx.off ? 0 :
    801             // 否则判断duration属性值是否为数字类型,是则使用 
    802             typeof opt.duration === "number" ? opt.duration :
    803             // 否则判断duration属性值字符串是否在jQuery.fx.speeds(jQuery的预配置动画时长)属性key字段中,是则使用 
    804             opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] :
    805             // 否则就是用默认动画时长
    806             jQuery.fx.speeds._default;
    807 
    808         // normalize opt.queue - true/undefined/null -> "fx"
    809         // 如果opt.queue的值是true/undefined/null之一,
    810         // 将其值设置为"fx"字符串,标示动画队列
    811         if (opt.queue == null || opt.queue === true) {
    812             opt.queue = "fx";
    813         }
    814 
    815         // Queueing
    816         // 将旧的回调(即我们添加的回调)存入opt.old
    817         opt.old = opt.complete;
    818 
    819         // 给opt.complete重新定义,
    820         // 在旧方法中通过装饰包装
    821         opt.complete = function() {
    822             if (jQuery.isFunction(opt.old)) {
    823                 // 执行我们的回调
    824                 opt.old.call(this);
    825             }
    826 
    827             // 如果有队列,执行我们下一个队列
    828             if (opt.queue) {
    829                 jQuery.dequeue(this, opt.queue);
    830             }
    831         };
    832 
    833         // 返回opt
    834         /*
    835         {complete, duration, easing, queue, old}
    836          */
    837         return opt;
    838     };
    839 
    840     jQuery.easing = {
    841         linear: function(p) {
    842             return p;
    843         },
    844         swing: function(p) {
    845             return 0.5 - Math.cos(p * Math.PI) / 2;
    846         }
    847     };
    848 
    849     // 全局timers数组,保存着所有动画tick
    850     jQuery.timers = [];
    851     jQuery.fx = Tween.prototype.init;
    852     // setInterval回调
    853     jQuery.fx.tick = function() {
    854         var timer, timers = jQuery.timers,
    855             i = 0;
    856 
    857         fxNow = jQuery.now();
    858 
    859         // 遍历所有tick
    860         for (; i < timers.length; i++) {
    861             timer = timers[i];
    862             // Checks the timer has not already been removed
    863             // 如果当前tick返回的为假(经弱转换)
    864             // 移除该tick
    865             // 然后继续遍历当前项,因为数组长度被改变了
    866             if (!timer() && timers[i] === timer) {
    867                 timers.splice(i--, 1);
    868             }
    869         }
    870 
    871         // 如果没有tick回调了,停止定时器
    872         if (!timers.length) {
    873             jQuery.fx.stop();
    874         }
    875         fxNow = undefined;
    876     };
    877 
    878     /**
    879      * 
    880      * 
    881      * @param  {Object} timer tick回调
    882      */
    883     jQuery.fx.timer = function(timer) {
    884         if (timer() && jQuery.timers.push(timer)) {
    885             jQuery.fx.start();
    886         }
    887     };
    888 
    889     jQuery.fx.interval = 13;
    890 
    891     // 动画正式开始
    892     jQuery.fx.start = function() {
    893         if (!timerId) {
    894             // 间隔执行jQuery.fx.tick
    895             timerId = setInterval(jQuery.fx.tick, jQuery.fx.interval);
    896         }
    897     };
    898 
    899     jQuery.fx.stop = function() {
    900         clearInterval(timerId);
    901         timerId = null;
    902     };
    903 
    904     jQuery.fx.speeds = {
    905         slow: 600,
    906         fast: 200,
    907         // Default speed
    908         _default: 400
    909     };
    910 
    911     // Back Compat <1.8 extension point
    912     jQuery.fx.step = {};
    913 
    914     if (jQuery.expr && jQuery.expr.filters) {
    915         jQuery.expr.filters.animated = function(elem) {
    916             return jQuery.grep(jQuery.timers, function(fn) {
    917                 return elem === fn.elem;
    918             }).length;
    919         };
    920     }
  • 相关阅读:
    C++中字符数组和字符指针问题
    C++中的常量指针和指针常量
    C++中指针形参问题
    Xcode视图调试Debug View Hierarchy
    第3章 程序的机器级表示(深入理解计算机系统)
    第2章 信息的表示和处理(深入理解计算机系统)
    第1章 计算机系统漫游(深入理解计算机系统)
    用gcc编译.cpp文件可能出现"undefined reference to `__gxx_personality_v0'"问题的解决
    第12章 并发编程(深入理解计算机系统)
    第11章 网络编程(深入理解计算机系统)
  • 原文地址:https://www.cnblogs.com/webFrontDev/p/3453021.html
Copyright © 2011-2022 走看看