亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

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

2019-11-20 11:05:23
字體:
來源:轉載
供稿:網友

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

相關教程:jQuery下的動畫處理總結: //www.49028c.com/article/42000.htm

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

a.動畫入口jQuery.fn.animate函數執行流程詳解

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

  先根據參數調用jQuery.speed獲取動畫相關參數,得到一個類似如下的對象;并且生成動畫執行函數doAnimation

optall = {  complete: fnction(){...},//動畫執行完成的回調  duration: 400,//動畫執行時長  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; 

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

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

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

b. jQuery內部函數Animation詳解

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

  Animation ( elem, properties, options ). properties是要進行動畫的css特征,options是動畫相關選項{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;}), 

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

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;  //執行動畫效果  for ( ; index < length ; index++ ) {    animation.tweens[ index ].run( percent );  }  //生成進度報告  deferred.notifyWith( elem, [ animation, percent, remaining ]);  if ( percent < 1 && length ) {    return remaining;  } else {    //動畫執行完畢,執行所有延時隊列中的函數(包括清除動畫相關的數據)    deferred.resolveWith( elem, [ animation ] );    return false;  }} 

  我們看到jQuery對動畫進度的處理:

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

  進度百分比 = 1 - 剩余時間百分比。

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

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

  實際上這種算法得到的時間n*13是不準確的,因為cpu不只是你一個程序在執行,時間片分給你的時候往往都比n*13大。而且是一個很不準確的值,導致動畫感覺時快時慢,不連貫。而jQuery這種方式保證當前的事件點上動畫執行結果的準確性,畢竟事件是最新計算結果。

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

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;  }}) 

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

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

  舉例說明propFilter修正成果。

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

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

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

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

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

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

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

  defaultPrefilter重點1:內聯元素中height/width相關動畫需要設置display特征值為inline-block

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

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

//對于height/width動畫overflow都要設置為"hidden",動畫完成后恢復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重點3:show/hide動畫的特殊處理:show/hide動畫調用genFx得到形如

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

  需要進行動畫處理的特征壓入handled列表,并將相應的特征刪除,后面會生成相應的緩動動畫。

for ( index in props ) {  value = props[ index ];   //rfxtypes = /^(?:toggle|show|hide)$/。可以看到最終只有和show/hide的動畫才會被饒茹handled中  if ( rfxtypes.exec( value ) ) {    delete props[ index ];    toggle = toggle || value === "toggle";    //如果當前節點的狀態和指定的狀態相同則不需要處理直接進行下一個狀態判斷    if ( value === ( hidden ? "hide" : "show" ) ) {      continue;    }    handled.push( index );  }}//有需要執行的動畫處理則進入分支,里面會對各個特征動畫生成緩動動畫length = handled.length;if ( length ) {  dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );  if ( "hidden" in dataShow ) {    hidden = dataShow.hidden;  }  // toggle需要保存狀態 - 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函數里面已經處理(上面的源碼)。

createTweens( animation, props ); 

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


  然后看一下經過createTweens之后的animation對象的tweens數組變成了

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

  第七,啟動動畫計時,定時執行tick

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

  最后,將傳入的動畫結束回調加入延時隊列

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

  Animation函數流程到此為止

拓展:

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

最終生成的attrs = {  height: "show",  marginTop: "show",  marginRight: "show",//當includeWidth為false時沒有  marginBottom: "show",  marginLeft: "show",//當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函數比較復雜,童鞋們可以隨便使用例子去跟蹤代碼。這個是理解jQuery源碼的一種比較好的方式。推薦兩個例子: 

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

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

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品r级在线| 在线视频欧美日韩精品| 成人国产亚洲精品a区天堂华泰| 在线观看日韩av| 欧美激情第1页| 欧美激情第99页| 国产日本欧美一区| 国产欧美一区二区三区视频| 精品久久久久久中文字幕大豆网| 综合欧美国产视频二区| 亚洲一区二区三区久久| 日韩av免费在线观看| 欧洲亚洲免费在线| 欧美性生交大片免网| 亚洲91精品在线| 国产色视频一区| 18一19gay欧美视频网站| 97国产精品人人爽人人做| 国产免费久久av| 91精品视频免费| 久久精品99无色码中文字幕| www.日韩系列| 欧美在线视频一区| 精品国产91久久久久久老师| 亚洲网址你懂得| 136fldh精品导航福利| 久久激情五月丁香伊人| 国产精品一区二区三区免费视频| 亚洲视频免费一区| 有码中文亚洲精品| 亚洲白虎美女被爆操| 国产一区玩具在线观看| 久久成人精品一区二区三区| 欧美电影免费在线观看| 亚洲精品aⅴ中文字幕乱码| 亚洲国产精品va在线| 福利视频一区二区| 成人天堂噜噜噜| 欧美亚洲国产另类| 日韩专区中文字幕| 国产精品久久中文| 欧美激情在线有限公司| 欧美一级淫片播放口| 久久久久久免费精品| 久久精品国产免费观看| 日韩激情av在线播放| 国产精品久久久久久久9999| 日本精品久久中文字幕佐佐木| 国产精品老牛影院在线观看| 亚洲欧美一区二区精品久久久| 欧美激情一区二区久久久| 欧美大片第1页| 亚洲色图色老头| 国产伊人精品在线| 国产精品免费在线免费| 国产日本欧美一区二区三区| 亚洲福利视频专区| 亚洲成人精品久久| 欧美性猛交xxxx乱大交蜜桃| 亚洲精品福利免费在线观看| 色播久久人人爽人人爽人人片视av| 在线观看国产精品91| 国产精品中文字幕在线观看| 欧美日韩精品在线| 国产精品男人爽免费视频1| 69国产精品成人在线播放| 久久国产精彩视频| 在线观看国产精品淫| 亚洲精品成人久久电影| 综合欧美国产视频二区| 91精品久久久久久| 17婷婷久久www| 国产精品一区二区三区成人| 中文字幕亚洲字幕| 国产福利视频一区二区| 最近2019好看的中文字幕免费| 亚洲少妇激情视频| 国产美女久久久| 国产精品网站视频| 日韩精品视频在线| 久久综合久久美利坚合众国| 久久精品视频va| 久久99精品视频一区97| 中文字幕日韩欧美精品在线观看| 粗暴蹂躏中文一区二区三区| 91精品免费看| 国产成人精品网站| 国产精品久久久久国产a级| 国产精品日韩在线一区| 欧美性一区二区三区| 欧美日韩一区二区在线| 日韩美女在线观看一区| 亚洲在线一区二区| 成人久久18免费网站图片| 日韩免费在线免费观看| 亚洲欧美在线免费| 亚洲欧洲国产伦综合| 日韩专区中文字幕| 国产精品入口福利| 国产成人在线播放| 91精品久久久久久久久久另类| 成人免费网站在线| 精品欧美激情精品一区| 一区二区三区久久精品| 91av在线播放视频| 国产mv免费观看入口亚洲| 国产亚洲欧洲在线| 国产成人aa精品一区在线播放| 性日韩欧美在线视频| 国产精品黄页免费高清在线观看| 亚洲色图第一页| 欧美极品xxxx| 欧美成人激情图片网| 亚洲午夜久久久久久久| www.日韩欧美| 久久久国产精品亚洲一区| 成人免费xxxxx在线观看| 韩曰欧美视频免费观看| 亚洲专区在线视频| 久热国产精品视频| 久久久这里只有精品视频| 成人激情黄色网| 精品久久香蕉国产线看观看gif| 国内精品久久久久影院 日本资源| 成人国产精品一区| 国产精品一区专区欧美日韩| 欧美激情亚洲另类| 国产一区二区三区精品久久久| 欧美激情久久久久久| 亚洲老头老太hd| 欧美高清性猛交| 疯狂做受xxxx欧美肥白少妇| 日本成人在线视频网址| 精品国产一区二区三区四区在线观看| 国产女精品视频网站免费| 亚洲国产精品va在线看黑人动漫| 国产免费一区二区三区在线能观看| 98精品国产自产在线观看| 国色天香2019中文字幕在线观看| 国产成人亚洲综合91精品| 国产丝袜精品第一页| 国产精品扒开腿做爽爽爽的视频| 亚洲成人黄色网| 亚洲自拍偷拍网址| 色一情一乱一区二区| 亚洲精品成人网| 久久久精品2019中文字幕神马| 91精品在线观看视频| 国产视频欧美视频| 国产有码在线一区二区视频| 91精品视频大全| 黑人与娇小精品av专区| 亚洲国产第一页| 亚洲精品国偷自产在线99热| 国产亚洲精品一区二区| 国产一区二区香蕉| 色综合久久88| 久久精品色欧美aⅴ一区二区| 亚洲深夜福利网站| 精品女同一区二区三区在线播放| 国产在线精品自拍| 日韩经典一区二区三区| 97热在线精品视频在线观看| 亚洲人成电影在线播放|