国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > JavaScript > 正文

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

2019-11-20 11:05:23
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

首先需要有隊(duì)列(queue)的基本知識(shí)。見(jiàn)上一章。

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

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

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

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

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

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

  沒(méi)有動(dòng)畫正在執(zhí)行則馬上執(zhí)行動(dòng)畫,否則將動(dòng)畫壓入動(dòng)畫隊(duì)列等待執(zhí)行

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

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

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

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

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

  首先,初始化一個(gè)延時(shí)對(duì)象,這個(gè)延時(shí)對(duì)象用來(lái)處理動(dòng)畫隊(duì)列。

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

  然后,生成一個(gè)每一個(gè)時(shí)間點(diǎn)(相鄰兩個(gè)時(shí)間點(diǎn)的事件間隔默認(rèn)為13毫秒)上都會(huì)執(zhí)行的函數(shù)tick,這個(gè)tick函數(shù)會(huì)保存在jQuery.timers中,然后每次執(zhí)行jQuery.fx.tick的時(shí)候會(huì)取出來(lái)執(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í)行動(dòng)畫效果  for ( ; index < length ; index++ ) {    animation.tweens[ index ].run( percent );  }  //生成進(jìn)度報(bào)告  deferred.notifyWith( elem, [ animation, percent, remaining ]);  if ( percent < 1 && length ) {    return remaining;  } else {    //動(dòng)畫執(zhí)行完畢,執(zhí)行所有延時(shí)隊(duì)列中的函數(shù)(包括清除動(dòng)畫相關(guān)的數(shù)據(jù))    deferred.resolveWith( elem, [ animation ] );    return false;  }} 

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

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

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

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

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

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

  第三,生成動(dòng)畫用的所有特征組成的對(duì)象animation(這個(gè)對(duì)象結(jié)構(gòu)如源碼所示),animation.props中保存的是用戶傳入的特征(動(dòng)畫最終目標(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特征名稱以便能被瀏覽器識(shí)別,其中需要注意的是borderWidth/padding/margin指的不是一個(gè)css特征,而是四個(gè)(上下左右)

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

  舉例說(shuō)明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做適配處理:比如對(duì)height/width的動(dòng)畫要求display和overflow為特定的值才能有效果;比如對(duì)show/hide動(dòng)畫需要對(duì)一大堆css特征值進(jìn)行動(dòng)畫,并且在函數(shù)里就調(diào)用createTweens生成緩動(dòng)動(dòng)畫。

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

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

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

// height/width overflow passif ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {  //確保沒(méi)有什么偷偷出來(lái)  //記錄3個(gè)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)動(dòng)畫需要設(shè)置display特征值為inline-block  if ( jQuery.css( elem, "display" ) === "inline" &&    jQuery.css( elem, "float" ) === "none" ) {    // 內(nèi)聯(lián)元素接受inline-block;    // 塊級(jí)元素必須內(nèi)嵌在布局上    if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {      style.display = "inline-block";    } else {      style.zoom = 1;    }  }} 

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

//對(duì)于height/width動(dòng)畫overflow都要設(shè)置為"hidden",動(dòng)畫完成后恢復(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動(dòng)畫的特殊處理:show/hide動(dòng)畫調(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)行動(dòng)畫處理的特征壓入handled列表,并將相應(yīng)的特征刪除,后面會(huì)生成相應(yīng)的緩動(dòng)動(dòng)畫。

for ( index in props ) {  value = props[ index ];   //rfxtypes = /^(?:toggle|show|hide)$/。可以看到最終只有和show/hide的動(dòng)畫才會(huì)被饒茹handled中  if ( rfxtypes.exec( value ) ) {    delete props[ index ];    toggle = toggle || value === "toggle";    //如果當(dāng)前節(jié)點(diǎn)的狀態(tài)和指定的狀態(tài)相同則不需要處理直接進(jìn)行下一個(gè)狀態(tài)判斷    if ( value === ( hidden ? "hide" : "show" ) ) {      continue;    }    handled.push( index );  }}//有需要執(zhí)行的動(dòng)畫處理則進(jìn)入分支,里面會(huì)對(duì)各個(gè)特征動(dòng)畫生成緩動(dòng)動(dòng)畫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 ];    //生成緩動(dòng)動(dòng)畫    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;      }    }  }} 

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

createTweens( animation, props ); 

  我們來(lái)看一看createTweens中具體做了什么,先看一下createTweens之前的animation對(duì)象


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

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

  第七,啟動(dòng)動(dòng)畫計(jì)時(shí),定時(shí)執(zhí)行tick

//啟動(dòng)動(dòng)畫計(jì)時(shí)jQuery.fx.timer(  jQuery.extend( tick, {    elem: elem,    anim: animation,    queue: animation.opts.queue  })); 

  最后,將傳入的動(dòng)畫結(jié)束回調(diào)加入延時(shí)隊(duì)列

//從options中獲取回調(diào)函數(shù)添加到延時(shí)隊(duì)列中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時(shí)獲取相關(guān)的需要?jiǎng)赢嫷奶卣鞯?/p>

最終生成的attrs = {  height: "show",  marginTop: "show",  marginRight: "show",//當(dāng)includeWidth為false時(shí)沒(méi)有  marginBottom: "show",  marginLeft: "show",//當(dāng)includeWidth為false時(shí)沒(méi)有  opacity: "show",  width: "show"} function genFx( type, includeWidth ) {  var which,    attrs = { height: type },    i = 0;  //如果包括寬度,步長(zhǎng)值為1來(lái)完成所有cssExpand值,  //如果不包括寬度,步長(zhǎng)值是2跳過(guò)左/右值  //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ù)雜,童鞋們可以隨便使用例子去跟蹤代碼。這個(gè)是理解jQuery源碼的一種比較好的方式。推薦兩個(gè)例子: 

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

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

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

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 紫云| 武隆县| 当雄县| 淅川县| 安康市| 班戈县| 龙门县| 巴青县| 福海县| 博乐市| 阜平县| 英超| 江达县| 洱源县| 临沧市| 老河口市| 莒南县| 临泉县| 孟村| 卢氏县| 永清县| 兴安县| 陈巴尔虎旗| 定日县| 沭阳县| 清新县| 保亭| 霍林郭勒市| 防城港市| 聂荣县| 枣庄市| 泗水县| 特克斯县| 南昌市| 屯门区| 新蔡县| 萨嘎县| 万源市| 略阳县| 临夏县| 平安县|