美文网首页
jQuery源码解析之$().animate()(上)

jQuery源码解析之$().animate()(上)

作者: 小进进不将就 | 来源:发表于2019-06-27 09:01 被阅读0次

    前言:
    需要先看 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()的流程图为:

    相关文章

      网友评论

          本文标题:jQuery源码解析之$().animate()(上)

          本文链接:https://www.haomeiwen.com/subject/bsniqctx.html