zoukankan      html  css  js  c++  java
  • jQuery源码学习12——动画加强

    实例化方法queue和原生方法dequeue实现了队列的管理

    当实现很复杂的动画时,队列管理显得很重要

    举例来说

    $("#div1").animate({
        "width":400,
        "height":300
    },300).animate({
        "left":600
    },300);

    这个动画希望先让#div1的宽变到400,高变到300,然后再让向右移动600

    需要注意的是在进行第一步操作的时候高一定会先到达目的值300,但是高到达目的值的时候宽不一定到了

    而且希望宽变成400,高变成300完事了以后再向右移动,而不是变宽变高的同时还移动着

    先看第一个animate的调用

    在animate内部,首先还是调用了queue实例化方法,传给queue的函数将作为一个队列函数存到DOM元素的queue.fx属性中

    第一次存进去的时候会顺便调用这个函数,调用的过程中给这个DOM元素绑定curAnim属性

    curAnim属性的值即为传进来的prop,在此处就是

    {
        "width":400,
        "height":300
    }

    接下来prop里面有几项,就会创建几个jQuery.fx对象

    在此会创建两个对象,暂且叫做widthfx对象和heightfx对象

    var e = new jQuery.fx( this, jQuery.speed(speed,callback), p );

    jQuery.fx的第一个参数是DOM对象,第二个参数是配置参数,第三个参数是要修改的样式的名称

    其中第二个参数形如

    {
        "duration":300,
        "oldComplete":undefined,
        "complete":function(){
            jQuery.dequeue(this,"fx");
        }
    }

    接下来执行实例化对象e的custom方法

            z.custom = function(from,to){
                z.startTime = (new Date()).getTime();
                z.now = from;
                z.a();
        
                z.timer = setInterval(function(){
                    z.step(from, to);
                }, 13);
            };

    custom方法里面又开了一个定时器调用了step方法

    但是第一次执行step也得等到13ms之后

    而js不会等着,不会什么都不干就等着13ms之后执行step

    定时器定义完毕之后custom方法执行完毕,继续执行animate里面的for in循环

    创建heightfx对象,然后调用heightfx对象的custom方法开一个定时器

    和widthfx对象一样,js也不会等着13ms之后执行step

    定时器定义完毕之后custom方法执行完毕,此时animate里面的for in循环也循环完了

    第一个animate就执行完了

    继续执行第二个animate

    和刚才的操作一样,也会调用queue方法,当执行完

    this.queue[type].push( fn );

    这一句时,通过分析我们可以知道这时this的queue.fx属性的值为

    [
        function(){
            //此处的prop是
            //{
            //    "width":400,
            //    "height":300
            //}
            this.curAnim = prop;
            for ( var p in prop ) {
                var e = new jQuery.fx( this, jQuery.speed(speed,callback), p );
                if ( prop[p].constructor == Number )
                    e.custom( e.cur(), prop[p] );
                else
                    e[ prop[p] ]( prop );
            }        
        },
        function(){
            //此处的prop是
            //{
            //    "left":600
            //}
            this.curAnim = prop;
            for ( var p in prop ) {
                var e = new jQuery.fx( this, jQuery.speed(speed,callback), p );
                if ( prop[p].constructor == Number )
                    e.custom( e.cur(), prop[p] );
                else
                    e[ prop[p] ]( prop );
            }        
        }
    ]

    这其实就是this(#div1)上面fx类型的队列,其中第一个函数所代表的width和height的运动正在进行

    再往下是实现队列很关键的一点:

                if ( this.queue[type].length == 1 )
                    fn.apply(this);

    这里很明显不符合if里面的条件,因此刚刚往this(#div1)的queue.fx属性中添加的队列函数并不执行

    等什么时候执行呢?会等第一个队列函数的width和height完事了之后才执行

    实现这个功能的原理就是函数回调,接下来一步一步分析

    这个queue方法接下来就执行完了,同时这第二个animate也就执行完了

    一切看起来似乎都完事了

    但是不要忘了刚才我们开了两个定时器

    从绑定时器那个时候开始过13ms之后,这两个定时器分别会触发

    所以两个animate执行完了之后真正的"动"画才刚开始

    需要注意的是两个定时器是分属两个不同的fx实例化对象的:

    z.custom = function(from,to){
        z.startTime = (new Date()).getTime();
        z.now = from;
        z.a();
    
        z.timer= setInterval(function(){
            z.step(from, to);
        }, 13);
    };

    因此看起来好像是两个定时器同时走,同时执行step函数,即#div1的width和height好像在同时改变

    每执行一次step函数,都会用时间戳判断是否到了目的值

    没有到目的值的话就每次递增或递减

    关键看到目的值的情况,准确的说是超过目的值的情况

    以heightfx对象的z.timer定时器为例

    首先,既然超了目的值,自然会关掉定时器

    并把定时器属性z.timer置为null,再将准确的目的值赋给对应的样式

    再将this(#div1)的curAnim属性里面的height子属性的值置为true

    此时需要注意,当height超过目的值的时候width还不一定到目的值

    因为height到300就可以了,而width得到400

    所以heightfx的定时器关掉了,而widthfx的定时器还在跑

    而且在接下来的for in循环中由于z.el.curAnim.height!==true的条件是成立的

    所以局部变量done变成了false

    于是接下来的两个if都不会执行

    分析到这里再往下也就很顺了

    当width也到目的值400的时候再次走这里的for in循环

    局部变量的值就是true了

    接下来的两个if就要执行了

    第一个没什么好说的,关键看第二个if

    if( done && z.o.complete && z.o.complete.constructor == Function )
        z.o.complete.apply( z.el );

    z.o.complete里面有一个操作是核心

    jQuery.dequeue(this, "fx");

    我们直接进到这个方法内部

        dequeue: function(elem,type){
            type = type || "fx";
        
            if ( elem.queue && elem.queue[type] ) {
                elem.queue[type].shift();
                var f = elem.queue[type][0];        
                if ( f ) f.apply( elem );
            }
        },

    前面我们分析到#div1上的queue.fx属性是一个数组,存放的是函数队列

    既然都走到这一步了,那么队列里面的第一个函数就执行完了

    queue.fx队列就会把第一个函数给shift掉

    再把这个队列里面现在的第一个队列函数取出来执行

    以此类推

    其实jQuery这样做达到了一个很好的效果,那就是避免了深层嵌套

    深层嵌套的工作在jQuery内部全部完成

  • 相关阅读:
    iTestin云测试工具
    android 存储操作 大小显示换算 kb mb KB MB 读取
    android 发送短信 判断号码规则 判断字符数70
    android 震动 各种
    10.13总结
    10.8每日总结
    10.9
    10.15
    10.14
    10.12每日总结
  • 原文地址:https://www.cnblogs.com/zhaohuiziwo901/p/5013811.html
Copyright © 2011-2022 走看看