摘要:根據(jù)的間隔,利用循環(huán)執(zhí)行,從而達(dá)到渲染動(dòng)畫的目的。最后,附上的流程圖,建議配合整個(gè)的流程圖二的最后一個(gè)圖一起看下篇將會(huì)模擬實(shí)現(xiàn)方法,敬請(qǐng)期待完
三、doAnimation內(nèi)部的Animation()方法
作用:
$().animate()核心方法
源碼:
//animate()核心方法 //源碼7844行 //elem:目標(biāo)元素 //this:目標(biāo)元素 //{"width": "500"} // optall={ // complete:function(){jQuery.dequeue()}, // old:false, // duration: 400, // easing: undefined, // queue:"fx", // } function Animation( elem, properties, options ) { var result, stopped, index = 0, //1 length = Animation.prefilters.length, //{ // always:function(){}, // catch:function(){}, // done:function(){}, // xxx // } //初始化deferred對(duì)象 //deferred.always()表示不管成功還是失敗,最終都會(huì)運(yùn)行內(nèi)部設(shè)置的代碼 deferred = jQuery.Deferred().always( function() { // Don"t match elem in the :animated selector delete tick.elem; } ), tick = function() { if ( stopped ) { return false; } var currentTime = fxNow || createFxNow(), remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), // Support: Android 2.3 only // Archaic crash bug won"t allow us to use `1 - ( 0.5 || 0 )` (#12497) temp = remaining / animation.duration || 0, percent = 1 - temp, index = 0, length = animation.tweens.length; for ( ; index < length; index++ ) { animation.tweens[ index ].run( percent ); } deferred.notifyWith( elem, [ animation, percent, remaining ] ); // If there"s more to do, yield if ( percent < 1 && length ) { return remaining; } // If this was an empty animation, synthesize a final progress notification if ( !length ) { deferred.notifyWith( elem, [ animation, 1, 0 ] ); } // Resolve the animation and report its conclusion deferred.resolveWith( elem, [ animation ] ); return false; }, //==========tick end========== //讓animation帶有promise的屬性,并在其中添加動(dòng)畫的屬性和方法 animation = deferred.promise( { elem: elem, props: jQuery.extend( {}, properties ), opts: jQuery.extend( true, { specialEasing: {}, easing: jQuery.easing._default }, options ), originalProperties: properties, originalOptions: options, startTime: fxNow || createFxNow(), duration: options.duration, tweens: [], //500,"width",animation createTween: function( prop, end ) { var tween = jQuery.Tween( elem, animation.opts, prop, end, animation.opts.specialEasing[ prop ] || animation.opts.easing ); animation.tweens.push( tween ); // { // easing: "swing" // elem: div#A // end: 500 // now: 500 // options: {specialEasing: {…}, easing: "swing", complete: ?, duration: 400, queue: "fx", …} // pos: 1 // prop: "width" // start: 100 // unit: "px" // } return tween; }, stop: function( gotoEnd ) { var index = 0, // If we are going to the end, we want to run all the tweens // otherwise we skip this part length = gotoEnd ? animation.tweens.length : 0; if ( stopped ) { return this; } stopped = true; for ( ; index < length; index++ ) { animation.tweens[ index ].run( 1 ); } // Resolve when we played the last frame; otherwise, reject if ( gotoEnd ) { deferred.notifyWith( elem, [ animation, 1, 0 ] ); deferred.resolveWith( elem, [ animation, gotoEnd ] ); } else { deferred.rejectWith( elem, [ animation, gotoEnd ] ); } return this; } } ), //===========animation end=============== props = animation.props; //{width:500},undefined propFilter( props, animation.opts.specialEasing ); for ( ; index < length; index++ ) { result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); if ( result ) { if ( isFunction( result.stop ) ) { jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = result.stop.bind( result ); } return result; } } /*運(yùn)行動(dòng)畫*/ // createTween(500,"width",animation) jQuery.map( props, createTween, animation ); if ( isFunction( animation.opts.start ) ) { animation.opts.start.call( elem, animation ); } // Attach callbacks from options animation .progress( animation.opts.progress ) .done( animation.opts.done, animation.opts.complete ) .fail( animation.opts.fail ) .always( animation.opts.always ); jQuery.fx.timer( //讓tick方法繼承elem、anim和queue屬性 jQuery.extend( tick, { elem: elem, anim: animation, queue: animation.opts.queue } ) ); return animation; }
解析:
(1)Animation.prefilters
源碼:
jQuery.Animation = jQuery.extend( Animation, { //源碼8175行 //defaultPrefilter是一個(gè)function prefilters: [ defaultPrefilter ], })
所以Animation.prefilters=1,defaultPrefilter的源碼暫不解析
(2)關(guān)于jQuery.Deferred()的解釋,請(qǐng)參考:jQuery中的Deferred詳解和使用
(3)jQuery.map(elems, callback, arg)
作用:
根據(jù)elems數(shù)量,循環(huán)運(yùn)行callback( elems[ i ], i, arg )
源碼:
jQuery.extend( { // arg is for internal usage only //源碼524行 //props, createTween, animation map: function( elems, callback, arg ) { var length, value, i = 0, ret = []; // Go through the array, translating each of the items to their new values //如果elems是類數(shù)組的話 if ( isArrayLike( elems ) ) { length = elems.length; for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret.push( value ); } } // Go through every key on the object, } else { //走這邊 for ( i in elems ) { //500 width animation /*執(zhí)行動(dòng)畫*/ value = callback( elems[ i ], i, arg ); if ( value != null ) { ret.push( value ); } } } console.log(ret,"ret555") // Flatten any nested arrays // 展平任何嵌套數(shù)組 return concat.apply( [], ret ); }, })
解析:
根據(jù)例子的話,就是:
createTween(500,"width",animation) createTween(300,"width",animation) createTween(1000,"width",animation)
(4)jQuery內(nèi)部函數(shù)createTween(value, prop, animation)
作用:
讓animation調(diào)用Animation.tweeners[ "*" ]中的方法
源碼:
//源碼7752行 //創(chuàng)建動(dòng)畫對(duì)象 // createTween(500,"width",animation) function createTween( value, prop, animation ) { var tween, //[ function( prop, value ) { // var tween = this.createTween( prop, value ); // console.log("vvvv","aaa8083") // adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); // return tween; // } ] collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), index = 0, //1 length = collection.length; for ( ; index < length; index++ ) { //prop:width //value:500 //運(yùn)行collection[ index ],this綁定animation if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { // We"re done with this property return tween; } } }
(5)Animation.tweeners[ "*" ]
作用:
讓animation調(diào)用Animation.tweeners[ "*" ]中的方法
jQuery.Animation = jQuery.extend( Animation, { //源碼8152行 tweeners: { //prop:width //value:500 "*": [ function( prop, value ) { //animation.createTween var tween = this.createTween( prop, value ); adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); return tween; } ] }, })
解析:
返回經(jīng)過animation. createTween("width",500)處理和adjustCSS()處理的變量tween
① animation. createTween("width",500)
animation是Animation()方法中封裝的一個(gè)對(duì)象(對(duì)象key的value是function)
作用:
根據(jù)開發(fā)者傳入的屬性,將其轉(zhuǎn)化為一個(gè)對(duì)象,對(duì)象內(nèi)部的屬性時(shí)執(zhí)行動(dòng)畫所需要的屬性。
源碼:
animation = deferred.promise( { //500,"width",animation createTween: function( prop, end ) { var tween = jQuery.Tween( elem, animation.opts, prop, end, animation.opts.specialEasing[ prop ] || animation.opts.easing ); animation.tweens.push( tween ); // { // easing: "swing" // elem: div#A // end: 500 // now: 500 // options: {specialEasing: {…}, easing: "swing", complete: ?, duration: 400, queue: "fx", …} // pos: 1 // prop: "width" // start: 100 // unit: "px" // } return tween; }, })
解析:
調(diào)用jQuery.Tween獲得tween對(duì)象,并把tween對(duì)象放進(jìn)animation.tweens數(shù)組中
② 簡單看下jQuery.Tween源碼:
//源碼7568行 function Tween( elem, options, prop, end, easing ) { //width 500 swing //width 300 swing //width 1000 swing 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 || jQuery.easing._default; this.options = options; this.start = this.now = this.cur(); this.end = end; this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); }, cur: function() {}, run: function( percent ) {}, }; Tween.prototype.init.prototype = Tween.prototype;
執(zhí)行jQuery.Tween方法,就是new一個(gè)對(duì)象,就是執(zhí)行jQuery.Tween.init()方法,根據(jù){width:500}生成的動(dòng)畫對(duì)象如下:
{ easing: "swing" elem: div#A end: 500 now: 500 options: {specialEasing: {…}, easing: "swing", complete: ?, duration: 400, queue: "fx", …} pos: 1 prop: "width" start: 100 unit: "px" }
③ 關(guān)于adjustCSS的解析,請(qǐng)看:jQuery源碼解析(4)—— css樣式、定位屬性
Animation.tweeners[ "*" ]方法最終返回的tween如下:
{ easing: "swing" elem: div#A end: 500 now: 500 options: {specialEasing: {…}, easing: "swing", complete: ?, duration: 400, queue: "fx", …} pos: 1 prop: "width" start: 100 unit: "px" }
綜上,jQuery.map()的最終作用就是將$().animate()中的參數(shù)轉(zhuǎn)化為動(dòng)畫對(duì)象,并push進(jìn)animation.tweens數(shù)組中
(6)jQuery.fx.timer()
作用:
依次執(zhí)行timer
源碼:
//源碼8504行 //單個(gè)動(dòng)畫內(nèi)部執(zhí)行 jQuery.fx.timer = function( timer ) { //將Animation.tick()依次放進(jìn)jQuery.timers數(shù)組中 jQuery.timers.push( timer ); //每push進(jìn)一個(gè),就運(yùn)行一個(gè) jQuery.fx.start(); };
jQuery.timers是一個(gè)數(shù)組:
//源碼8431行 jQuery.timers = [];
(7)jQuery.fx.start()
作用:
在動(dòng)畫運(yùn)行前,加鎖,并運(yùn)行動(dòng)畫
源碼:
//源碼8514行 //加鎖,運(yùn)行動(dòng)畫 jQuery.fx.start = function() { if ( inProgress ) { return; } //動(dòng)畫開始即為運(yùn)行中,加上鎖 inProgress = true; //運(yùn)行 schedule(); };
注意:inProgress 鎖是控制整個(gè)動(dòng)畫流程的鎖,而不是單個(gè)動(dòng)畫隊(duì)列的鎖
(8)schedule()
作用:
如果動(dòng)畫已經(jīng)開始(inProgress=true),那么就不斷執(zhí)行jQuery.fx.tick()方法(動(dòng)畫渲染)
源碼:
//源碼7694行 //如果動(dòng)畫已經(jīng)開始,那么就不斷執(zhí)行jQuery.fx.tick()方法(動(dòng)畫渲染) function schedule() { //inProgress是判斷整個(gè)動(dòng)畫流程是否結(jié)束的標(biāo)志 //當(dāng)inProgress=null時(shí),整個(gè)動(dòng)畫結(jié)束 if ( inProgress ) { //走這邊 if ( document.hidden === false && window.requestAnimationFrame ) { //使用requestAnimationFrame來完成動(dòng)畫 //遞歸 window.requestAnimationFrame( schedule ); } else { //13代表動(dòng)畫每秒運(yùn)行的幀數(shù),可以保證瀏覽器能完成動(dòng)畫 //jQuery.fx.interval = 13; window.setTimeout( schedule, jQuery.fx.interval ); } /*執(zhí)行動(dòng)畫*/ jQuery.fx.tick(); } }
(9)jQuery.fx.tick()
作用:
運(yùn)行Animation.tick()并安全地移除它
源碼:
//源碼8483行 //運(yùn)行Animation.tick()并安全地移除它 jQuery.fx.tick = function() { var timer, i = 0, timers = jQuery.timers; fxNow = Date.now(); //這里的timers,就是Animation.tick()的集合 for ( ; i < timers.length; i++ ) { timer = timers[ i ]; // Run the timer and safely remove it when done (allowing for external removal) //運(yùn)行Animation.tick()并安全地移除它 if ( !timer() && timers[ i ] === timer ) { timers.splice( i--, 1 ); } } //inProgress=null,停止動(dòng)畫 if ( !timers.length ) { jQuery.fx.stop(); } fxNow = undefined; }; //源碼8474行 //結(jié)束整個(gè)動(dòng)畫流程 jQuery.fx.stop = function() { inProgress = null; };
(10)Animation.tick()
作用:
根據(jù)動(dòng)畫的參數(shù)來執(zhí)行動(dòng)畫
源碼:
function Animation( elem, properties, options ) { //根據(jù)動(dòng)畫的參數(shù)來執(zhí)行動(dòng)畫 tick = function() { if ( stopped ) { return false; } //當(dāng)前時(shí)間的時(shí)間戳 var currentTime = fxNow || createFxNow(), //動(dòng)畫時(shí)長默認(rèn)400ms //開始時(shí)間+動(dòng)畫時(shí)長-當(dāng)前時(shí)間 //在每次調(diào)用requestAnimationFrame后,記錄下剩下的的時(shí)間在總時(shí)間(duration)中的位置 remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), // Support: Android 2.3 only // Archaic crash bug won"t allow us to use `1 - ( 0.5 || 0 )` (#12497) //剩下的時(shí)間占總時(shí)長的占比 temp = remaining / animation.duration || 0, //當(dāng)前時(shí)間占總時(shí)長的占比 percent = 1 - temp, index = 0, length = animation.tweens.length; for ( ; index < length; index++ ) { //根據(jù)傳入的動(dòng)畫參數(shù)和當(dāng)前進(jìn)程的百分比來運(yùn)行動(dòng)畫 animation.tweens[ index ].run( percent ); } deferred.notifyWith( elem, [ animation, percent, remaining ] ); // If there"s more to do, yield if ( percent < 1 && length ) { return remaining; } // If this was an empty animation, synthesize a final progress notification if ( !length ) { deferred.notifyWith( elem, [ animation, 1, 0 ] ); } // Resolve the animation and report its conclusion deferred.resolveWith( elem, [ animation ] ); return false; }, }
解析:
通過動(dòng)畫持續(xù)時(shí)間duration、動(dòng)畫開始時(shí)間animation.startTime和每次調(diào)用requestAnimationFrame后動(dòng)畫結(jié)束時(shí)間currentTime,計(jì)算出此幀在整個(gè)動(dòng)畫流程中的占比,從而較為準(zhǔn)確繪制動(dòng)畫
(11)Tween.run()
作用:
繪制動(dòng)畫幀
源碼:
Tween.prototype = { run: function( percent ) { // { // easing: "swing" // elem: div#A // end: 500 // now: 105.52601592046467 // options: {specialEasing: {…}, easing: "swing", complete: ?, duration: 400, queue: "fx", …} // pos: 1 // prop: "width" // start: 100 // unit: "px" // } var eased, //undefiend hooks = Tween.propHooks[ this.prop ]; //400 if ( this.options.duration ) { //swing,兩邊慢中間快 //動(dòng)畫效果 this.pos = eased = jQuery.easing[ this.easing ]( percent, this.options.duration * percent, 0, 1, this.options.duration ); } else { this.pos = eased = percent; } //width的寬度 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 { //走這邊 //執(zhí)行style變化 Tween.propHooks._default.set( this ); } return this; }, }
解析:
一個(gè)是動(dòng)畫效果swing的處理:jQuery.easing[ this.easing ](percent, this.options.duration * percent, 0, 1, this.options.duration);
另一個(gè)就是關(guān)鍵的style變化了:Tween.propHooks._default.set( this )
(12)Tween.propHooks._default.set()
作用:
執(zhí)行style變化
源碼:
Tween.propHooks = { _default: { //源碼7661行 set: function( tween ) { // Use step hook for back compat. // Use cssHook if its there. // Use .style if available and use plain properties where available. //undefined if ( jQuery.fx.step[ tween.prop ] ) { jQuery.fx.step[ tween.prop ]( tween ); } else if ( tween.elem.nodeType === 1 && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { //走這邊 //#A,width,100px(103px,134px,xxx) jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); } else { tween.elem[ tween.prop ] = tween.now; } }, } }
解析:
tween.now,是每次requestAnimationFrame要變化的width的值,tween.unit是px,所以這段代碼最終執(zhí)行的是jQuery.style( 目標(biāo)元素, 要變化的style屬性, 要變化的值 )
(13)jQuery.style()
作用:
設(shè)置 DOM 節(jié)點(diǎn)的 style 屬性
簡略的源碼:
// Get and set the style property on a DOM Node //源碼7279行 style: function( elem, name, value, extra ) { elem.style[ name ] = value }
綜上,Animation() 有兩大作用:
(1)將傳入的動(dòng)畫對(duì)象處理成jQuery的動(dòng)畫對(duì)象。
(2)根據(jù)duration的間隔,利用requestAnimationFrame循環(huán)執(zhí)行style,從而達(dá)到渲染動(dòng)畫的目的。
最后,附上 doAnimation() 的流程圖,建議配合整個(gè)$().animate()的流程圖(二、的最后一個(gè)圖)一起看:
下篇將會(huì)模擬實(shí)現(xiàn)$().animate() 方法,敬請(qǐng)期待!
(完)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/105056.html
摘要:前言需要先看源碼解析之和一舉例的寬度先變成,再變成,最后變成這是在異步調(diào)用中,進(jìn)行同步調(diào)用動(dòng)畫是異步的就是連續(xù)調(diào)用二作用通過樣式將元素從一個(gè)狀態(tài)改變?yōu)榱硪粋€(gè)狀態(tài)源碼之前有說過是的方法源碼行是否是空對(duì)象,方法執(zhí)行單個(gè)動(dòng)畫的封裝的本質(zhì)是執(zhí)行 showImg(https://segmentfault.com/img/remote/1460000019594521); 前言:需要先看 jQue...
摘要:前言在上篇的基礎(chǔ)上,接入邏輯圖實(shí)現(xiàn)之的實(shí)現(xiàn)這是匿名函數(shù)自調(diào)用,下面好長好長的就是也就是說是一個(gè)這里也是匿名函數(shù)自調(diào)用本質(zhì)就是經(jīng)過一系列操作得到并作為參數(shù),賦值給匹配初始化模仿動(dòng)畫效果兩頭慢,中間快創(chuàng)建動(dòng)畫緩動(dòng)對(duì)象動(dòng)畫緩動(dòng)算法 showImg(https://segmentfault.com/img/remote/1460000019626160); 前言:在上篇的基礎(chǔ)上,接入doAni...
閱讀 1599·2021-11-22 09:34
閱讀 1689·2019-08-29 16:36
閱讀 2668·2019-08-29 15:43
閱讀 3113·2019-08-29 13:57
閱讀 1296·2019-08-28 18:05
閱讀 1874·2019-08-26 18:26
閱讀 3242·2019-08-26 10:39
閱讀 3453·2019-08-23 18:40