欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

jQuery 1.9.1源碼分析系列(十五)之動畫處理

 更新時間:2015年12月03日 11:32:43   作者:chua1989  
這篇文章主要介紹了jQuery 1.9.1源碼分析系列(十五)之動畫處理 的相關(guān)資料,需要的朋友可以參考下

首先需要有隊列(queue)的基本知識。見上一章。

相關(guān)教程:jQuery下的動畫處理總結(jié): http://www.dbjr.com.cn/article/42000.htm

jQuery 1.9.1源碼分析系列(十五)動畫處理之緩動動畫核心Tween  :http://www.dbjr.com.cn/article/75821.htm

a.動畫入口jQuery.fn.animate函數(shù)執(zhí)行流程詳解

--------------------------------------------------------------------------------

  先根據(jù)參數(shù)調(diào)用jQuery.speed獲取動畫相關(guān)參數(shù),得到一個類似如下的對象;并且生成動畫執(zhí)行函數(shù)doAnimation

optall = {
  complete: fnction(){...},//動畫執(zhí)行完成的回調(diào)
  duration: 400,//動畫執(zhí)行時長
  easing: "swing",//動畫效果
  queue: "fx",//動畫隊列
  old: false/fnction(){...},
} 
var empty = jQuery.isEmptyObject( prop ),
  optall = jQuery.speed( speed, easing, callback ),
  doAnimation = function() {
    //在特征的副本上操作,保證每個特征效果不會被丟失
    var anim = Animation( this, jQuery.extend( {}, prop ), optall );
    doAnimation.finish = function() {
      anim.stop( true );
    };
    //空動畫或完成需要立馬解決
    if ( empty || jQuery._data( this, "finish" ) ) {
      anim.stop( true );
    }
  };
doAnimation.finish = doAnimation; 

  沒有動畫正在執(zhí)行則馬上執(zhí)行動畫,否則將動畫壓入動畫隊列等待執(zhí)行

//沒有動畫在執(zhí)行則馬上執(zhí)行動畫,否則將動畫壓入動畫隊列等待執(zhí)行
return empty || optall.queue === false ?
  this.each( doAnimation ) :
  this.queue( optall.queue, doAnimation ); 

  可以看出,真正執(zhí)行動畫的地方是Animation( this, jQuery.extend( {}, prop ), optall )函數(shù)

b. jQuery內(nèi)部函數(shù)Animation詳解

--------------------------------------------------------------------------------

  Animation ( elem, properties, options ). properties是要進(jìn)行動畫的css特征,options是動畫相關(guān)選項(xiàng){complete: function () {…},duration: 400,easing: undefined,old: false,queue: "fx"}。

  首先,初始化一個延時對象,這個延時對象用來處理動畫隊列。

deferred = jQuery.Deferred().always( function() {
  // don't match elem in the :animated selector
  delete tick.elem;
}), 

  然后,生成一個每一個時間點(diǎn)(相鄰兩個時間點(diǎn)的事件間隔默認(rèn)為13毫秒)上都會執(zhí)行的函數(shù)tick,這個tick函數(shù)會保存在jQuery.timers中,然后每次執(zhí)行jQuery.fx.tick的時候會取出來執(zhí)行。

tick = function() {
  if ( stopped ) {
    return false;
  }
  var currentTime = fxNow || createFxNow(),
    remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
    // 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;
  //執(zhí)行動畫效果
  for ( ; index < length ; index++ ) {
    animation.tweens[ index ].run( percent );
  }
  //生成進(jìn)度報告
  deferred.notifyWith( elem, [ animation, percent, remaining ]);
  if ( percent < 1 && length ) {
    return remaining;
  } else {
    //動畫執(zhí)行完畢,執(zhí)行所有延時隊列中的函數(shù)(包括清除動畫相關(guān)的數(shù)據(jù))
    deferred.resolveWith( elem, [ animation ] );
    return false;
  }
} 

  我們看到j(luò)Query對動畫進(jìn)度的處理:

remaining = Math.max( 0, animation.startTime + animation.duration - currentTime )temp = remaining / animation.duration || 0,percent = 1 - temp, 

  進(jìn)度百分比 = 1 - 剩余時間百分比。

  平常我們是這么處理:假設(shè)時間13毫秒執(zhí)行一次動畫,當(dāng)前是第n此執(zhí)行,總的動畫時長為T。那么 

  進(jìn)度百分比 = (n*13)/T

  實(shí)際上這種算法得到的時間n*13是不準(zhǔn)確的,因?yàn)閏pu不只是你一個程序在執(zhí)行,時間片分給你的時候往往都比n*13大。而且是一個很不準(zhǔn)確的值,導(dǎo)致動畫感覺時快時慢,不連貫。而jQuery這種方式保證當(dāng)前的事件點(diǎn)上動畫執(zhí)行結(jié)果的準(zhǔn)確性,畢竟事件是最新計算結(jié)果。

  第三,生成動畫用的所有特征組成的對象animation(這個對象結(jié)構(gòu)如源碼所示),animation.props中保存的是用戶傳入的特征(動畫最終目標(biāo))。

animation = deferred.promise({
  elem: elem,
  props: jQuery.extend( {}, properties ),
  opts: jQuery.extend( true, { specialEasing: {} }, options ),
  originalProperties: properties,
  originalOptions: options,
  startTime: fxNow || createFxNow(),
  duration: options.duration,
  tweens: [],
  createTween: function( prop, end ) {
    var tween = jQuery.Tween( elem, animation.opts, prop, end,
      animation.opts.specialEasing[ prop ] || animation.opts.easing );
    animation.tweens.push( tween );
    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.resolveWith( elem, [ animation, gotoEnd ] );
    } else {
      deferred.rejectWith( elem, [ animation, gotoEnd ] );
    }
    return this;
  }
}) 

  第四,調(diào)用propFilter修正css特征名稱以便能被瀏覽器識別,其中需要注意的是borderWidth/padding/margin指的不是一個css特征,而是四個(上下左右)

//經(jīng)過propFilter,animation.opts.specialEasing添加了相應(yīng)的特征
propFilter( props, animation.opts.specialEasing ); 

  舉例說明propFilter修正成果。

  例1,css特征{ height: 200 }的修正后結(jié)果為:

props = { height: 200 }
animation.opts.specialEasing = {height: undefined} 

  例2:,css特征{margin:200}的修正結(jié)果為:

props = { marginBottom: 200,marginLeft: 200,marginRight: 200,marginTop: 200 }
animation.opts.specialEasing = { marginBottom: undefined,marginLeft: undefined,marginRight: undefined,marginTop: undefined } 

  第五,調(diào)用defaultPrefilter做適配處理:比如對height/width的動畫要求display和overflow為特定的值才能有效果;比如對show/hide動畫需要對一大堆css特征值進(jìn)行動畫,并且在函數(shù)里就調(diào)用createTweens生成緩動動畫。

// animationPrefilters[0] = defaultPrefilter
for ( ; index < length ; index++ ) {
  result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
  if ( result ) {
    return result;
  }
} 

  其中animationPrefilters[ index ]值得函數(shù)就是defaultPrefilter,defaultPrefilter函數(shù)處理有幾個比較重要的地方

  defaultPrefilter重點(diǎn)1:內(nèi)聯(lián)元素中height/width相關(guān)動畫需要設(shè)置display特征值為inline-block

// height/width overflow pass
if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
  //確保沒有什么偷偷出來
  //記錄3個overflow相關(guān)特征,因?yàn)镮E不能改變overflow特征值,
  //當(dāng)overflowX和overflowY設(shè)置了相同的值
  opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
  // 內(nèi)聯(lián)元素中height/width相關(guān)動畫需要設(shè)置display特征值為inline-block
  if ( jQuery.css( elem, "display" ) === "inline" &&
    jQuery.css( elem, "float" ) === "none" ) {
    // 內(nèi)聯(lián)元素接受inline-block;
    // 塊級元素必須內(nèi)嵌在布局上
    if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
      style.display = "inline-block";
    } else {
      style.zoom = 1;
    }
  }
} 

  defaultPrefilter重點(diǎn)2:對于height/width動畫overflow都要設(shè)置為"hidden",動畫完成后恢復(fù)。這個有利于提高渲染速度。

//對于height/width動畫overflow都要設(shè)置為"hidden",動畫完成后恢復(fù)
if ( opts.overflow ) {
  style.overflow = "hidden";
  //收縮包裝塊
  if ( !jQuery.support.shrinkWrapBlocks ) {
    anim.always(function() {
      style.overflow = opts.overflow[ 0 ];
      style.overflowX = opts.overflow[ 1 ];
      style.overflowY = opts.overflow[ 2 ];
    });
  }
} 

  defaultPrefilter重點(diǎn)3:show/hide動畫的特殊處理:show/hide動畫調(diào)用genFx得到形如

props = {
      height: "hide"
      marginBottom: "hide"
      marginLeft: "hide"
      marginRight: "hide"
      marginTop: "hide"
      opacity: "hide"
      paddingBottom: "hide"
      paddingLeft: "hide"
      paddingRight: "hide"
      paddingTop: "hide"
      width: "hide"
    } 

  需要進(jìn)行動畫處理的特征壓入handled列表,并將相應(yīng)的特征刪除,后面會生成相應(yīng)的緩動動畫。

for ( index in props ) {
  value = props[ index ];  

 //rfxtypes = /^(?:toggle|show|hide)$/??梢钥吹阶罱K只有和show/hide的動畫才會被饒茹handled中
  if ( rfxtypes.exec( value ) ) {
    delete props[ index ];
    toggle = toggle || value === "toggle";
    //如果當(dāng)前節(jié)點(diǎn)的狀態(tài)和指定的狀態(tài)相同則不需要處理直接進(jìn)行下一個狀態(tài)判斷
    if ( value === ( hidden ? "hide" : "show" ) ) {
      continue;
    }
    handled.push( index );
  }
}
//有需要執(zhí)行的動畫處理則進(jìn)入分支,里面會對各個特征動畫生成緩動動畫
length = handled.length;
if ( length ) {
  dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );
  if ( "hidden" in dataShow ) {
    hidden = dataShow.hidden;
  }
  // toggle需要保存狀態(tài) - enables .stop().toggle() to "reverse"
  if ( toggle ) {
    dataShow.hidden = !hidden;
  }
  if ( hidden ) {
    jQuery( elem ).show();
  } else {
    anim.done(function() {
      jQuery( elem ).hide();
    });
  }
  anim.done(function() {
    var prop;
    jQuery._removeData( elem, "fxshow" );
    for ( prop in orig ) {
      jQuery.style( elem, prop, orig[ prop ] );
    }
  });
  for ( index = 0 ; index < length ; index++ ) {
    prop = handled[ index ];
    //生成緩動動畫
    tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );
    orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
    if ( !( prop in dataShow ) ) {
      dataShow[ prop ] = tween.start;
      if ( hidden ) {
        tween.end = tween.start;
        tween.start = prop === "width" || prop === "height" ? 1 : 0;
      }
    }
  }
} 

  第六,生成緩動動畫,show/hide在defaultPrefilter函數(shù)里面已經(jīng)處理(上面的源碼)。

createTweens( animation, props ); 

  我們來看一看createTweens中具體做了什么,先看一下createTweens之前的animation對象


  然后看一下經(jīng)過createTweens之后的animation對象的tweens數(shù)組變成了

  將margin分解成了四個屬性(marginTop/Right/Bottom/Left)并且每個屬性都有自己的動畫特征。

  第七,啟動動畫計時,定時執(zhí)行tick

//啟動動畫計時
jQuery.fx.timer(
  jQuery.extend( tick, {
    elem: elem,
    anim: animation,
    queue: animation.opts.queue
  })
); 

  最后,將傳入的動畫結(jié)束回調(diào)加入延時隊列

//從options中獲取回調(diào)函數(shù)添加到延時隊列中
return animation.progress( animation.opts.progress )
  .done( animation.opts.done, animation.opts.complete )
  .fail( animation.opts.fail )
  .always( animation.opts.always ); 

  Animation函數(shù)流程到此為止

拓展:

  前面提到的genFx函數(shù)是專門用在toggle、hide、show時獲取相關(guān)的需要動畫的特征的

最終生成的attrs = {
  height: "show",
  marginTop: "show",
  marginRight: "show",//當(dāng)includeWidth為false時沒有
  marginBottom: "show",
  marginLeft: "show",//當(dāng)includeWidth為false時沒有
  opacity: "show",
  width: "show"
} 
function genFx( type, includeWidth ) {
  var which,
    attrs = { height: type },
    i = 0;
  //如果包括寬度,步長值為1來完成所有cssExpand值,
  //如果不包括寬度,步長值是2跳過左/右值
  //cssExpand = [ "Top", "Right", "Bottom", "Left" ]
  includeWidth = includeWidth? 1 : 0;
  for( ; i < 4 ; i += 2 - includeWidth ) {
    which = cssExpand[ i ];
    attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
  }
  if ( includeWidth ) {
    attrs.opacity = attrs.width = type;
  }
  return attrs;
} 

  Animation函數(shù)比較復(fù)雜,童鞋們可以隨便使用例子去跟蹤代碼。這個是理解jQuery源碼的一種比較好的方式。推薦兩個例子: 

  第一個,有hide/show的例子:$("#id").hide(1000);

  第二個,其他例子:$("#id").animate({"marginLeft":500},1000);

jQuery 1.9.1源碼分析系列(十五)之動畫處理 的全部內(nèi)容就給大家介紹到這里,有問題隨時給我留言,謝謝。!

相關(guān)文章

最新評論