1. 程式人生 > >Jquery動畫(animate)的使用及擴展說明

Jquery動畫(animate)的使用及擴展說明

思路 毫秒 eat sha typeof 來看 包含 const show

JQuery動畫可以實現非常多的效果,並且支持擴展動畫效果,其中 http://easings.net/zh-cn# 在基於JQuery上作了非常有用的動畫擴展,尤其在一些曲線或拋物線上沒有這些公式很難做出理想的動畫來。

JQuery動畫的底層實現核心思路是把整個區間分割成n個時間段,按時間動畫關系函數來計算出當時所在移動(變化)的區間比率值。
技術分享圖片
這是一個easings自由掉落動畫曲線,橫向為時間,縱向為移動(變化)區間,在不同的時間裏計算出對應的移動(變化)區間比例值即可實現對應的動畫效果。
下面來看看底層部分代碼:

動畫入口代碼:

jQuery.fn.extend({
    fadeTo: function( speed, to, easing, callback ) {

        // show any hidden elements after setting opacity to 0
        return this.filter( isHidden ).css( "opacity", 0 ).show()

            // animate to the value specified
            .end().animate({ opacity: to }, speed, easing, callback );
    },
    animate: function( prop, speed, easing, callback ) {
        var empty = jQuery.isEmptyObject( prop ),
            optall = jQuery.speed( speed, easing, callback ),
            doAnimation = function() {
                // Operate on a copy of prop so per-property easing won‘t be lost
                var anim = Animation( this, jQuery.extend( {}, prop ), optall );

                // Empty animations resolve immediately
                if ( empty ) {
                    anim.stop( true );
                }
            };

        return empty || optall.queue === false ?
            this.each( doAnimation ) :
            this.queue( optall.queue, doAnimation );
    },
    stop: function( type, clearQueue, gotoEnd ) {
        var stopQueue = function( hooks ) {
            var stop = hooks.stop;
            delete hooks.stop;
            stop( gotoEnd );
        };

        if ( typeof type !== "string" ) {
            gotoEnd = clearQueue;
            clearQueue = type;
            type = undefined;
        }
        if ( clearQueue && type !== false ) {
            this.queue( type || "fx", [] );
        }

        return this.each(function() {
            var dequeue = true,
                index = type != null && type + "queueHooks",
                timers = jQuery.timers,
                data = jQuery._data( this );

            if ( index ) {
                if ( data[ index ] && data[ index ].stop ) {
                    stopQueue( data[ index ] );
                }
            } else {
                for ( index in data ) {
                    if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
                        stopQueue( data[ index ] );
                    }
                }
            }

            for ( index = timers.length; index--; ) {
                if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
                    timers[ index ].anim.stop( gotoEnd );
                    dequeue = false;
                    timers.splice( index, 1 );
                }
            }

            // start the next in the queue if the last step wasn‘t forced
            // timers currently will call their complete callbacks, which will dequeue
            // but only if they were gotoEnd
            if ( dequeue || !gotoEnd ) {
                jQuery.dequeue( this, type );
            }
        });
    }
});

可以看到 animate 方法是基於 Animation 對象實現的,但在創建 Animation 對象時需要一個 jQuery.speed( speed, easing, callback ) 返回值,這個返回值(即是動畫的時間與區間關系函數),在做動畫擴展時也是擴展這個返回值的種類。下面再來看看 jQuery.speed做了什麽:

jQuery.speed = function( speed, easing, fn ) {
    var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
        complete: fn || !fn && easing ||
            jQuery.isFunction( speed ) && speed,
        duration: speed,
        easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
    };

    opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
        opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;

    // normalize opt.queue - true/undefined/null -> "fx"
    if ( opt.queue == null || opt.queue === true ) {
        opt.queue = "fx";
    }

    // Queueing
    opt.old = opt.complete;

    opt.complete = function() {
        if ( jQuery.isFunction( opt.old ) ) {
            opt.old.call( this );
        }

        if ( opt.queue ) {
            jQuery.dequeue( this, opt.queue );
        }
    };

    return opt;
};

jQuery.easing = {
    linear: function( p ) {
        return p;
    },
    swing: function( p ) {
        return 0.5 - Math.cos( p*Math.PI ) / 2;
    }
};

上面的代碼中做了一些初始化處理,其中也包含了一些默認的數據,包括默認支持動畫,以及默認動畫時長,我們在調用動畫時就可以在對應的參數上使用這些鍵名如:$(‘div‘).animate({top:‘100px‘}, ‘slow‘);
在 jQuery.speed 處理中會提取 opt.duration 區間值,這個值就是時長以毫秒為單位,常規默認值如下,如果不指定動畫時長則默認為400毫秒。即在調用動畫時可以寫 $(‘div‘).animate({top:‘100px‘});

jQuery.fx.speeds = {
    slow: 600,
    fast: 200,
    // Default speed
    _default: 400
};

然後就是 Animation 對象,這個對象只是一個對jQuery.Tween對象的包裝,以及動畫定時器啟停處理,所以就不貼代碼了,直接看jQuery.Tween代碼

function Tween( elem, options, prop, end, easing ) {
    return new Tween.prototype.init( elem, options, prop, end, easing );
}
jQuery.Tween = Tween;

Tween.prototype = {
    constructor: Tween,
    init: function( elem, options, prop, end, easing, unit ) {
        this.elem = elem;
        this.prop = prop;
        this.easing = easing || "swing";
        this.options = options;
        this.start = this.now = this.cur();
        this.end = end;
        this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
    },
    cur: function() {
        var hooks = Tween.propHooks[ this.prop ];

        return hooks && hooks.get ?
            hooks.get( this ) :
            Tween.propHooks._default.get( this );
    },
    run: function( percent ) {
        var eased,
            hooks = Tween.propHooks[ this.prop ];

        if ( this.options.duration ) {
            // 動畫函數調用
            this.pos = eased = jQuery.easing[ this.easing ](
                percent, this.options.duration * percent, 0, 1, this.options.duration
            );
        } else {
            this.pos = eased = percent;
        }
        this.now = ( this.end - this.start ) * eased + this.start;

        if ( this.options.step ) {
            this.options.step.call( this.elem, this.now, this );
        }

        if ( hooks && hooks.set ) {
            hooks.set( this );
        } else {
            Tween.propHooks._default.set( this );
        }
        return this;
    }
};

Tween.prototype.init.prototype = Tween.prototype;

在這段代碼中是最終調用動畫函數(代碼中已經添加註釋),這個動畫調用函數也是我們擴展時需要遵循的,下面來說下擴展函數的參數:

jQuery.easing[ this.easing ](percent, this.options.duration * percent, 0, 1, this.options.duration)

從參數名上就可以定義一些意思:
percent 當前動畫完成時長占比占比值(0% ~ 100%)當然這裏是以小數來表示如 0.6
this.options.duration * percent 當前動畫完成時長
0 返回最小值
1 返回最大值
this.options.duration 動畫總時長
需要說明下,這個函數不需要元素移動(變化)的樣式值,只需要動畫的時間進度,通過時間進度得出一個返回最大與最小範圍內的值通過這個值乘以移動(變化的總差值)可以計算出本次的移動變化位置,從而實現動畫。註意當 percent 超過 100% 時返回值會隨著超過返回最大值。

那麽在擴展動畫時應該是怎麽樣處理?

jQuery.extend(jQuery.easing, {
     ‘動畫名‘ : function (percent, present, minReturn, maxReturn, duration){
                         return  處理函數公式 ;
         },
         ...
})

調用方式:

 $(‘div‘).animate({top:‘100px‘}, 1000, ‘動畫名‘);

到這裏可以說如果我們不在基於JQuery上執行一些JQuery的擴展動畫函數時就可以按照上面的參數說明來做,當前在做時一定要註意元素樣式單位值(最好保證一致性),還有需要註意元素的style屬性值與css文件的樣式值獲取方法不一樣,這裏就不一一說明。

下面給出一個自由掉落原生使用代碼,動畫函數取自easings,其它函數類似方式使用

    (function () {
        //動畫函數
        function easeOutBounce(n, e, t, a, i) {
            return(e /= i) < 1 / 2.75 ? a * (7.5625 * e * e) + t : e < 2 / 2.75 ? a * (7.5625 * (e -= 1.5 / 2.75) * e + .75) + t : e < 2.5 / 2.75 ? a * (7.5625 * (e -= 2.25 / 2.75) * e + .9375) + t : a * (7.5625 * (e -= 2.625 / 2.75) * e + .984375) + t;
        }
        var percent = 0, duration = 1000, t, div = document.createElement(‘div‘), diff = 600;
        div.style.width = ‘10px‘;
        div.style.height = ‘10px‘;
        div.style.position = ‘absolute‘;
        div.style.top = ‘0px‘;
        div.style.left = ‘50%‘;
        div.style.backgroundColor = ‘red‘;
        t = setInterval(function () {
            var _percent = percent / 100,
                    per = easeOutBounce(_percent, duration * _percent, 0, 1, duration);
            div.style.top = Math.round(diff * per) + ‘px‘;
            percent += 1;
            if (percent > 100) {
                clearInterval(t);
            }
        }, 20);
        document.body.appendChild(div);
        console.info(document.body);
    })();

Jquery動畫(animate)的使用及擴展說明