前言:
需要先看 jQuery源码解析之$.queue()、$.dequeue()和jQuery.Callbacks()

一、举例
divA 的宽度先变成 500px,再变成 300px,最后变成 1000px:

<script src="jQuery.js"></script>  <div id="A" style="width:100px;height:50px;background-color: deeppink">这是A</div><script>  let A = document.querySelector('#A');  //在异步调用中,进行同步调用  //动画是异步的  A.onclick = function() {    //就是连续调用animation.add()    $('#A').animate({      'width': '500'    }).animate({      'width': '300'    }).animate({      'width': '1000'    });  };</script>

二、$().animate()
作用:
通过 CSS 样式将元素从一个状态改变为另一个状态

源码:

  //之前有说过: jQuery.fn.extend() 是$()的方法  jQuery.fn.extend( {   //源码8062行    //{'width': '500'}    animate: function( prop, speed, easing, callback ) {      //是否是空对象,false      var empty = jQuery.isEmptyObject( prop ),        // optall={        //   complete:function(){jQuery.dequeue()},        //   old:false,        //   duration: 400,        //   easing: undefined,        //   queue:"fx",        // }                //undefined undefined undefined        optall = jQuery.speed( speed, easing, callback ),        doAnimation = function() {                   // Operate on a copy of prop so per-property easing won't be lost          //Animation 方法执行单个动画的封装          //doAnimation的本质是执行Animation方法          //this:目标元素          //{'width': '500'}          //optall={xxx}          var anim = Animation( this, jQuery.extend( {}, prop ), optall );          // Empty animations, or finishing resolves immediately          //finish是数据缓存的一个全局变量          //如果为true,立即终止动画          if ( empty || dataPriv.get( this, "finish" ) ) {            anim.stop( true );          }        };      //注意这个      //自身的.finish=自身      doAnimation.finish = doAnimation;      return empty || optall.queue === false ?        this.each( doAnimation ) :        //一般走这里        //通过 queue 调度动画之间的衔接        //optall.queue:"fx"        //doAnimation:function(){}        this.queue( optall.queue, doAnimation );    },  })

解析:
(1)jQuery.speed()
作用:
初始化动画对象的属性

源码:

  //源码8009行  //undefiend undefined undefined  //作用是返回一个经过修改的opt对象  jQuery.speed = function( speed, easing, fn ) {    // opt={    //   complete:false,    //   duration: undefined,    //   easing: undefined,    // }    var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {      complete: fn || !fn && easing ||        isFunction( speed ) && speed,      duration: speed,      easing: fn && easing || easing && !isFunction( easing ) && easing    };        // Go to the end state if fx are off    /*这边是为opt.duration赋值的*/    //undefiend    if ( jQuery.fx.off ) {      opt.duration = 0;    } else {      if ( typeof opt.duration !== "number" ) {        if ( opt.duration in jQuery.fx.speeds ) {          opt.duration = jQuery.fx.speeds[ opt.duration ];        } else {          //走这边          //_default=400          opt.duration = jQuery.fx.speeds._default;        }      }    }    /*为opt.queue赋值*/    // Normalize opt.queue - true/undefined/null -> "fx"    //注意这个==,是模糊判断    if ( opt.queue == null || opt.queue === true ) {      opt.queue = "fx";    }    // Queueing    /*为opt.old赋值*/    opt.old = opt.complete;    opt.complete = function() {      if ( isFunction( opt.old ) ) {        opt.old.call( this );      }      //如果queue有值得话,就出队列      //因为queue默认为"fx",所以一般会触发dequeue操作      if ( opt.queue ) {        //this指目标元素        //opt.queue        jQuery.dequeue( this, opt.queue );      }    };    //此时的opt为:    // opt={    //   complete:function(){jQuery.dequeue()},    //   old:false,    //   duration: 400,    //   easing: undefined,    //   queue:"fx",    // }    return opt;  };

解析:
通过传入的三个参数,对opt对象进行处理,如果三个参数都为undefined的话,opt等于:

   opt={       complete:function(){jQuery.dequeue()},       old:false,       duration: 400,       easing: undefined,       queue:"fx",   }

(2)animate内部做了什么?
作用:
animate内部封装了一个doAnimation触发器,触发器触发就会运行Animation方法,animate最后返回的是queue()方法,注意queue()方法的参数带有触发器doAnimation

(3)$().queue()
作用:
执行入队操作(jQuery.queue()),如果是fx动画的话,同时执行出队操作(jQuery.dequeue()

源码
这个方法上篇文章已经分析过了,这里就简单分析下:

  jQuery.fn.extend( {    //optall.queue:"fx"    //doAnimation:function(){}    //fx动画的话,就执行dequeue(),也就是队首callback函数    queue: function( type, data ) {          //返回的是数组[doAnimate,doAnimate,xxx]          var queue = jQuery.queue( this, type, data );           //初始化hooks          jQuery._queueHooks( this, type );          /*专门为fx动画做处理*/          //如果队首没有锁的话,直接运行队首元素          if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {            jQuery.dequeue( this, type );          }    },

解析:
inprogress是动画队列的锁,目的是保证上个动画执行结束后,再去执行下个动画

每入队一个doAnimate函数,如果队首没有inprogress 锁的话,就会出队去运行一个doAnimate函数

jQuery._queueHooks()的意义在于添加一个empty.remove()方法,用来清空队列queue

(4)jQuery.queue()
作用:
上篇文章也分析过了,就是将doAnimate函数pushqueue数组中

(5)jQuery.dequeue()
作用:
如果队首元素不是inprogress,而是doAnimation方法,则先将doAnimation出队,再让inprogress入队首,再运行doAnimation方法;
如果队首元素是inprogress的话,则移除锁

如果队列为空的话,则清空queue,节省内存

源码:

   //源码4624行    //目标元素,'type'    dequeue: function( elem, type ) {      //'type'      type = type || "fx";      //get,获取目标元素的队列      var queue = jQuery.queue( elem, type ),        //长度        startLength = queue.length,        //去除对首元素,并返回该元素        fn = queue.shift(),        //确保该队列有一个hooks        hooks = jQuery._queueHooks( elem, type ),        //next相当于dequeue的触发器        next = function() {          jQuery.dequeue( elem, type );        };      // If the fx queue is dequeued, always remove the progress sentinel      //如果fn='inprogress',说明是fx动画队列正在出队,就移除inprogress      if ( fn === "inprogress" ) {        fn = queue.shift();        startLength--;      }      if ( fn ) {        // Add a progress sentinel to prevent the fx queue from being        // automatically dequeued        //如果是fx动画队列的话,就添加inprogress标志,来防止自动出队执行        //意思应该是等上一个动画执行完毕后,再执行下一个动画        if ( type === "fx" ) {          queue.unshift( "inprogress" );        }        // Clear up the last queue stop function        //删除hooks的stop属性方法        delete hooks.stop;        //递归dequeue方法        fn.call( elem, next, hooks );      }      console.log(startLength,'startLength4669')      //如果队列是一个空数组,并且hooks存在的话,清除该队列      if ( !startLength && hooks ) {        //进行队列清理        hooks.empty.fire();        console.log(hooks.empty.fire(),'bbbb4671')      }    },

解析:
循环同步运行多个doAnimation()方法,直到队列为空

综上,除doAnimation内的逻辑外,整个$().animate()的流程图为: