国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

求索:GSAP的動畫快于jQuery嗎?為何?

LiangJ / 2222人閱讀

摘要:本文已完結,請看下文求索的動畫快于嗎為何續本文源自對問題動畫性能優于的原理是什么的回答。是這樣的嗎請看下文求索的動畫快于嗎為何續

  

本文已完結,請看下文: > 求索:GSAP的動畫快于jQuery嗎?為何?/續

本文源自對問題《GSAP js動畫性能優于jQuery的原理是什么?》的回答。GSAP是一個js動畫插件,它聲稱“20x faster than jQuery”,是什么讓它這么快呢?

每當有這樣的問題的時候,我們可以通過以下步驟來確定一個未知的解決方案的性能優化是怎么做到/偽造的:

黑盒:從官方用例來看,究竟有多快,快在哪兒

白盒:看看官方用例之內,框架怎么做到優化的

do { 提出假設,自己構建用例測試 } while (假設沒有得到驗證);

得出結論

文中提到的timer、recalculate、layout、repaint、composite layer,需要瀏覽器內部運行相關的基礎知識。見:

《瀏覽器的渲染原理簡介》

《瀏覽器的工作原理:新式網絡瀏覽器幕后揭秘》

黑盒:從用例來看,究竟有多快,快在哪兒

首先我們打開chrome,并開啟官網的H5動畫速度測試頁面:http://www.greensock.com/js/speed.html。

頁面中用js計算出的fps很不準確,還是以瀏覽器的統計為準。

在jQuery和GSAP兩個框架下打開,然后點run,然后f12審查元素,進入Timeline頁面,點record。過了100frame以后暫停,然后進入頁面點擊stop。

以下是jQuery的結果:100幀6.53s,平均FPS:15幀/秒
(也可以自己算出來 100frames ÷ 6.53s ≈ 15.3FPS

以下是GSAP的結果:100幀2.22s,平均FPS:45幀/秒。比jQuery快2倍呢。

來對比一下100幀里面各個流程的耗時(單位:秒):

類目 詳情 jQuery GSAP
scripting timer等js執行 2.87 0.52
rendering recalculate(重計算)、layout(回流) 2.04 0.77
painting repaint(重繪)、composite layers(混合圖層) 0.88 0.78
loading 加載 0 0
other stuff 未知 0.06 0.11

我們來看看前3幀里面兩個框架都發生了什么:

jQuery:

GSAP:

看來GSAP比起jQuery主要的性能優化在下面這兩個類目:

JS:主要是timer,jQuery里面,每幀大概有10~20個timer被觸發(并維持在67ms左右);GSAP每幀不超過6個timer(并短于30ms);

渲染:jQuery沒有layout步驟,但是GSAP有,而且每次都影響到整個文檔;jQuery的recalculate步驟,每次僅影響1個元素;而GSAP每次影響到170左右的元素。

GSAP的渲染詳情內容:

jQuery的渲染詳情內容:

這樣看來,timer造成了很大的區別,而渲染部分本應沒有太大區別(layout由于動畫部分是position:absolute,影響范圍不大),但是二者的最終差異也比較大,我們只有通過源碼和用例看到區別了。

白盒:看看用例之內,GSAP框架怎么做到優化的 帶著懷疑看看用例

先看看測試頁面jQuery和GSAP的用例:

//jQuery
jQuery.easing.cubicIn = $.easing.cubicIn = function( p, n, firstNum, diff ) { //we need to add the standard CubicIn ease to jQuery
   return firstNum + p * p * p * diff;
}
jQuery.fx.interval = 10; //ensures that jQuery refreshes at roughly 100fps like GSAP, TweenJS, and most of the others to be more even/fair.
tests.jquery = {
  milliseconds:true,
  wrapDot:function(dot) {
    return jQuery(dot); //wrap the dot in a jQuery object in order to perform better (that way, we don"t need to query the dom each time we tween - we can just call animate() directly on the jQuery object)
  },
  tween:function(dot) {
    dot[0].style.cssText = startingCSS;
    var angle = Math.random() * Math.PI * 2;
    dot.delay(Math.random() * duration).animate({left:Math.cos(angle) * radius + centerX, 
           top:Math.sin(angle) * radius + centerY, 
           width:32, 
           height:32}, duration, "cubicIn", function() { tests.jquery.tween(dot) });
  },
  stop:function(dot) {
    dot.stop(true);
  },
  nativeSize:false
};

//GSAP (TweenLite) top/left/width/height
tests.gsap = {
  milliseconds:false,
  wrapDot:function(dot) {
    return dot; //no wrapping necessary
  },
  tween:function(dot) {
    var angle = Math.random() * Math.PI * 2;
    dot.style.cssText = startingCSS;
    TweenLite.to(dot, duration, {css:{left:Math.cos(angle) * radius + centerX, 
                      top:Math.sin(angle) * radius + centerY, 
                      width:32, 
                      height:32},
                   delay:Math.random() * duration,
                   ease:Cubic.easeIn,
                   overwrite:"none",
                   onComplete:tests.gsap.tween,
                   onCompleteParams:[dot]});
  },
  stop:function(dot) {
    TweenLite.killTweensOf(dot);
  },
  nativeSize:false
};

function toggleTest() {
   inProgress = !inProgress;
   var i;
   if (inProgress) {
      currentTest = tests[engineInput.value];
      size = (currentTest.nativeSize ? "16px" : "1px");
      centerX = jQuery(window).width() / 2;
      centerY = (jQuery(window).height() / 2) - 30;
      startingCSS = "position:absolute; left:" + centerX + "px; top:"
         + centerY + "px; width:" + size + "; height:" + size + ";"; 
      radius = Math.sqrt(centerX * centerX + centerY * centerY);
      duration = Number(durInput.value);

      createDots();
      i = dots.length;
      while (--i > -1) {
         currentTest.tween(dots[i]);
      }
   }
}

jQuery部分除了時間函數"CubicIn"我們平常用不上以外,其他的部分都符合我們的正常使用習慣。注意到jQuery的jQuery.fx.interval,也就是MsPF(millisecond per frame,我編的單位)被調到了10,換言之,FPS是100。

  

“以讓測試更加公平”,注釋說。

雪姨:“好大的口氣”

讓我們接著看源碼……

JS運行優化

之前的觀測結果表明:JS部分中,主要是timer:jQuery里面,每幀大概有10~20個timer被觸發,并維持在67ms左右;GSAP每幀不超過6個timer,同時每幀短于30ms。

我在自己的一個空白頁面引用了jQuery的1.10.2的未壓縮版,然后用chrome打開頁面,并在console輸入jQuery.Animation,回車,查看它的定義。并一步步查看其中我覺得有可能會帶我到定時器的函數定義,直到得到結果。

在這個過程中,我知道了,jQuery的Animation采用的定時器是setInterval:

jQuery.Animation = function Animation( elem, properties, options ) {
  // 上部分省略...
  jQuery.fx.timer(
    jQuery.extend( tick, {
      elem: elem,
      anim: animation,
      queue: animation.opts.queue
    })
  );

  // attach callbacks from options
  return animation.progress( animation.opts.progress )
    .done( animation.opts.done, animation.opts.complete )
    .fail( animation.opts.fail )
    .always( animation.opts.always );
}

jQuery.fx.timer = function ( timer ) {
   if ( timer() && jQuery.timers.push( timer ) ) {
      jQuery.fx.start();
   }
} 

jQuery.fx.start = function () {
  if ( !timerId ) {
    timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
  }
} 

jQuery.fx.interval = 13;

就算我們不在這個項目里調節jQuery.fx.interval到10,原生的jQuery.fx.interval居然是一13ms/frame,換算成FPS就是77,要知道有一些瀏覽器的繪制上限是60FPS,即1000ms ÷ 60frame ≈ 16.7 ms/frame,這個interval會要求一些瀏覽器在繪制上限內執行1.3次,瀏覽器每隔幾幀會丟棄掉其中的1次,而這就造成了額外的損耗,這也是在上面的現象中jQuery里面timer過分耗時,被喚起的次數在20次左右的原因。

而GSAP沒有猜錯的話,應該是用到requestAnimationFrame(以及低版本IE下的setTimeout作為polyfill),并盡可能剪短定時器內部內容(jQuery處于兼容性考慮,會做大量條件判斷,這方面自然會敗給GSAP),來壓榨定時器性能的。

我們在源碼中搜requestAnimationFrame,在TweenLite.js中:

/* Ticker
 */
var _reqAnimFrame = window.requestAnimationFrame, 
_cancelAnimFrame = window.cancelAnimationFrame, 
_getTime = Date.now || function() {return new Date().getTime();},
_lastUpdate = _getTime();

//now try to determine the requestAnimationFrame and cancelAnimationFrame functions and if none are found, we"ll use a setTimeout()/clearTimeout() polyfill.
a = ["ms","moz","webkit","o"];
i = a.length;
while (--i > -1 && !_reqAnimFrame) {
  _reqAnimFrame = window[a[i] + "RequestAnimationFrame"];
  _cancelAnimFrame = window[a[i] + "CancelAnimationFrame"] || window[a[i] + "CancelRequestAnimationFrame"];
}

_class("Ticker", function(fps, useRAF) {
  var _self = this,
    _startTime = _getTime(),
    _useRAF = (useRAF !== false && _reqAnimFrame),
    _fps, _req, _id, _gap, _nextTime,
    _tick = function(manual) {
      _lastUpdate = _getTime();
      _self.time = (_lastUpdate - _startTime) / 1000;
      var overlap = _self.time - _nextTime,
        dispatch;
      if (!_fps || overlap > 0 || manual === true) {
        _self.frame++;
        _nextTime += overlap + (overlap >= _gap ? 0.004 : _gap - overlap);
        dispatch = true;
      }
      if (manual !== true) {
        //make sure the request is made before we dispatch the "tick" event so that
        //timing is maintained.
        //Otherwise, if processing the "tick" requires a bunch of time (like 15ms)
        //and we"re using a setTimeout() that"s based on 16.7ms,
        //it"d technically take 31.7ms between frames otherwise.
        _id = _req(_tick);
      }
      if (dispatch) {
        _self.dispatchEvent("tick");
      }
    };
  // ...
  _self.wake = function() {
    if (_id !== null) {
      _self.sleep();
    }
    _req = (_fps === 0) ? _emptyFunc : (!_useRAF || !_reqAnimFrame) ? function(f) { return setTimeout(f, ((_nextTime - _self.time) * 1000 + 1) | 0); } : _reqAnimFrame;
    if (_self === _ticker) {
      _tickerActive = true;
    }
    _tick(2);
  };
}

這段代碼是非常典型的requestAnimationFrame的polyfill。并且在polyfill部分,計算了瀏覽器的繪制上限的時間間隔,也符合我之前的猜測。

渲染優化

之前的觀測結果表明,渲染部分,jQuery沒有layout步驟,但是GSAP有,而且每次都影響到整個文檔;jQuery的recalculate步驟,每次僅影響1個元素;而GSAP每次影響到170左右的元素。

從觀測結果來看,GSAP做的是化零為整,一次性重新布局全部元素的活兒(一次性改變它們的top、left值,甚至有可能是重新替換了一整個DOM內部的全部HTML)。

是這樣嗎?GSAP的源碼太過龐大,我們怎么構造對代碼結構的感性認識呢?

我把官方用例保存到了本地,用的是xxx.htm,這樣會生成一個xxx_files文件夾,里面有所有引用的資源文件。(很有意思,png沒有保存下來)。然后我用沒有壓縮過的源代碼文件替代了TweenLite.min.jsCSSPlugin.min.js

現在我再生成一次timeline:

這個時候觸發Recalculate style的代碼行數與調用棧非常清晰了。我點進p.setRatio@CSSPlugin.min.js:2066,在當前行新建一個斷點,然后刷新頁面:

調用棧與上下文都出現了,這個時候的源碼是清晰可讀的。上下文是沒有innerHTML或者$.html之類的代碼,我在這里知道,沒有采用一次性刷新innerHTML的方法(事實上,這樣做的代價也很高)。這里每一步改變的都是CSSStyleDeclaration,但這個CSSStyleDeclaration沒有連接到相應元素。

仔細閱讀調用棧每一層的上下文之后,我做出了它分層的依據:

調用棧(自頂向下) 代碼理解
_tick 重繪時間管理層,用rAF函數繪制一幀
EventDispatcher.dispatchEvent 事件處理層,用事件代替回調
Animation._updateRoot 動畫管理層,在這里還會每隔一定幀數做一次gc
SimpleTimeline.render 時間線管理層,鏈式幀的結構
TweenLite.render 幀管理層,本幀和下一幀的引用,計算Tween值
CSSPlugin.setRatio CSS樣式管理層,處理CSS樣式最終的格式

對jQuery的測試用例做同樣的事情,可以看到,在最底端,也就是觸發recalculate的一端,style直接引用的是DOM元素的style。

jQuery.extend.style = function( elem, name, value, extra ) {
  var ret, type, hooks,
    origName = jQuery.camelCase( name ),
    style = elem.style;
  name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
  hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];

  if ( value !== undefined ) {
    //style里面的一大堆判斷省略
    style[ name ] = value;
  }
}

畫出相應的架構:

調用棧(自頂向下) 代碼理解
jQuery.dequeue.next jQuery隊列函數
jQuery.fn.animate Animate函數,這里建立Animation對象
jQuery.fx.timer 定時器管理,在這里緩存所有的定時器
jQuery.fx.start 使用setInterval開始一個定時器
Animation.tick Animation對象,管理幀和tween值(中間值)的關系
jQuery.Tween.run Tween對象,處理中間值和時間函數的關系
jQuery.Tween.propHooks.set 抽象set函數,以set各種prop
jQuery.style set函數的實例化,處理元素的style

意識到了嗎,jQuery是過程化的,每個函數/類代表一個需要管理/控制兼容性的需求。

假設與驗證,自己構建測試用例,看看優化是否名不副實

綜上所述,我們得到以下假設:

jQuery的定時器采用的是setInterval,受到瀏覽器重繪上限的控制,而GSAP采用requestAnimationFrame,完全將重繪交給瀏覽器管理,以獲得更好地重繪性能

jQuery每次都是多帶帶修改一個DOM的style,而GSAP是計算離線的style,然后再賦給DOM。這導致了不一樣的時間開銷。

jQuery沒有集中繪制,每個DOM都在一個事件回調函數上下文中處理,有多少個DOM就有多少個上下文;GSAP有集中繪制。同時jQuery是過程化的,GSAP是面向對象的。這讓jQuery非常難以做到集中控制繪制。jQuery會將DOM的引用一路傳遞到最終改變DOM的style函數中,這在調用過程中也會非常浪費空間。這些也都導致了不一樣的時間開銷。

是這樣的嗎?

請看下文: 求索:GSAP的動畫快于jQuery嗎?為何?/續

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/110843.html

相關文章

  • 求索GSAP動畫快于jQuery?/ 續 V1.1

    摘要:本文是求索的動畫快于嗎為何的續文。沒有集中繪制,每個都在一個事件回調函數上下文中處理,有多少個就有多少個上下文有集中繪制。測試過程中為了比較好的效果用了隨機數。 本文是求索:GSAP的動畫快于jQuery嗎?為何? 的續文。GSAP是一個js動畫插件,它聲稱20x faster than jQuery,是什么讓它這么快呢? 每當有這樣的問題的時候,我們可以通過以下步驟來...

    Tecode 評論0 收藏0
  • requestAnimationFrame Web中寫動畫另一種選擇

    摘要:現在又多了一種實現動畫的方案,那就是還在草案當中的方法。這個方法就是傳遞給的回調函數。為回調函數一個簡單的例子模擬一個進度條動畫,初始寬度為在函數中將進度加然后再更新到寬度上,在進度達到之前,一直重復這一過程。 HTML5/CSS3時代,我們要在web里做動畫選擇其實已經很多了: 你可以用CSS3的animattion+keyframes; 你也可以用css3的transition...

    Prasanta 評論0 收藏0
  • requestAnimationFrame Web中寫動畫另一種選擇

    摘要:現在又多了一種實現動畫的方案,那就是還在草案當中的方法。這個方法就是傳遞給的回調函數。為回調函數一個簡單的例子模擬一個進度條動畫,初始寬度為在函數中將進度加然后再更新到寬度上,在進度達到之前,一直重復這一過程。 HTML5/CSS3時代,我們要在web里做動畫選擇其實已經很多了: 你可以用CSS3的animattion+keyframes; 你也可以用css3的transition...

    alin 評論0 收藏0
  • requestAnimationFrame Web中寫動畫另一種選擇

    摘要:現在又多了一種實現動畫的方案,那就是還在草案當中的方法。這個方法就是傳遞給的回調函數。為回調函數一個簡單的例子模擬一個進度條動畫,初始寬度為在函數中將進度加然后再更新到寬度上,在進度達到之前,一直重復這一過程。 HTML5/CSS3時代,我們要在web里做動畫選擇其實已經很多了: 你可以用CSS3的animattion+keyframes; 你也可以用css3的transition...

    piapia 評論0 收藏0
  • GSAP - 專業 Web 動畫

    摘要:雖然沒有視覺效果,但這就是基本的值動畫。有專門的位置可以查詢緩動函數。另外,不要期望在不支持的瀏覽器上做動畫。是專業動畫庫,在大部分情況下,它也具備更好的動畫性能。 說到在網頁里創建動畫,你可能很快會想到jQuery的animate()方法,或者css3的animation和transition。現在,本文將介紹另一個web動畫的可選方案,GSAP。 GSAP的全名是GreenSock...

    MASAILA 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<