今天咱們就借用PIXI.JS的源碼來講說以上功能的實現(本文不打算講解TS,爲了方(tou)便(lan)就是用v4版本的代碼)canvas
本文要講解的是core/ticker目錄中的Ticker和TickerListener他們用來處理畫面的動態更新,以及執行每次更新時的回調。app
在閱讀PIXI.JS的上手文檔時,你第一個接觸的就是一下這行代碼:函數
var app = new PIXI.Application();oop
這裏咱們實例化的app,後續會用來添加精靈圖,製做動畫。動畫
咱們都知道,依賴canvas的動畫是會不斷循環執行loop函數,把圖像繪製在畫布上,以此實現動畫。那麼Application是如何實現的?this
在構造函數中,咱們能夠看到一個內部變量this._ticker變量,這個變量就是實現loop的核心對象,它經過this.ticker對外暴露。spa
經過set方法,咱們知道,對ticker賦值實際上是更新了this._ticker(用新值覆蓋了前一次的this._ticker)。而start方法也是執行了this._ticker.start。code
而經過ticker.add方法,application將render函數添加到了渲染隊列中,此後每次執行loop是都會促發一個render函數的執行:對象
那麼這裏的add
,start
,remove
三個方法都幹了什麼?這個UPDATE_PRIORITY.LOW
又是什麼?blog
記住這幾個問題本篇咱們會一一解答。
那咱們就看看Ticker.js都實現了什麼邏輯。
ps:這裏的shared其實也是Ticker的實例:
Ticker.js在core/ticker文件夾下:
core文件夾是用來存放核心代碼的,好比diaplay中存放處理顯示盒的代碼,sprites中是處理精靈圖的代碼,renderer中存放處理渲染的代碼。這些咱們往後都會說到。上一節咱們說的new Application()的邏輯代碼就來源於core根目錄的Application.js。
core/ticker文件夾下除了Ticker外,還有TickerListener和index。上一節說的shared就是在index中定義的:
而TickerListener是專門用來處理Ticker中的監聽回調隊列的(後面會講到)
話很少說讓咱們看看Ticker(這裏我刪除一些代碼,只留下核型邏輯):
這裏最重要的就是內部變量this._head和this._tick;
咱們以前說過Ticker內部實現了一個渲染回調隊列,在每次loop是都須要執行一遍,而這個this._head就是這個這個隊列的源頭,至於爲何須要單獨製做一個源頭,等咱們說完TickerListener你就能理解其中精妙了。而咱們在appliction中傳進來的render函數,也會做爲TickerListener插入到這個隊列之中:
而這個顯然目前這個隊列中除了head外只有一個Listener。這些回調會在每次loop後執行一邊以更新畫布上的圖像。而這個loop就由this._tick開啓。
this._tick會不斷被requestAnimationFrame調用執行,以實現loop的邏輯。可是循環開啓有三個條件:this.started爲真,this._requestId = null以及this._head.next存在,這三個條件是否都成立?
還記得咱們在Appliction.js中有調用this.start方法?
Application.js
this.start實際上是對_ticker.start的封裝。
這裏的start是一個單例,未開始時會執行一次循環,並把this.started設爲真,這樣即使重複執行start方法也不會在促發更新。這樣咱們解答了上文的第一個問題,start
函數的邏輯(還剩下add
,remove
邏輯,和UPDATE_PRIORITY.LOW
的意義)
每次this._tick 執行時都會把他設爲null,因此loop的第二個條件也知足,
這裏的next也是一個TickerListener,咱們前面說過TickerListener是一個回調函數鏈,不斷經過next找到下一個回調知道執行完回調函數鏈。這個咱們在說到TickerListener會詳細說明。當咱們在Application.js中調用add方法時,咱們就已經往插入了TickerListener,因此this._head.next也存在。那麼這個loop就會一直循環下去,直到有邏輯觸發關閉。
那麼this.update(time);處理了什麼邏輯?
這裏經過next遍歷回調函數鏈來執行emit方法。這些邏輯都被寫在TickerListener中,不過在進入TickerListener以前咱們還須要弄懂add和remove函數邏輯以及UPDATE_PRIORITY.LOW的意義。
add
函數和它的姐妹函數addOnce
底層都調用了內部方法this._addListener。
這裏TickerListener能夠接受4個參數,第四個參數用於控制回調是執行一次仍是可以反覆執行。
而此前傳入UPDATE_PRIORITY.LOW
實際上是能夠控制回調函數權重的:
一共有5檔,默認狀況下回調是按順序添加,先添加的回調先執行,但也能夠經過控制傳入的權重大小影響回調函數插入的位置(下面會解釋),但若是PRIORITY一致時,就是先添加的先執行。
而TickerListener具體邏輯在TickerListener源碼解析一篇中會詳細解釋。那麼咱們就來看看內部方法_addListener:
在前面分析update邏輯時,咱們能夠看到TickerListener回調隊列是不斷經過找尋下一個next來完成鏈式執行。而具體TickerListener的位置由previous和next決定,除了_head外TickerListener能夠沒有next卻不能沒有previous。(甚至兩個TickerListener的previous有多是同一個)。
而執行完插入邏輯後,會執行this._startIfPossible();
這保證了,若是autoStart爲真,當咱們往_header後添加了TickerListener後,但即使咱們沒有執行start函數(this.started爲false),邏輯上也會開啓loop。
但若是咱們已經執行完start後(this.started爲true),此時再執行_requestIfNeeded;
由於每次執行完_tick後this._requestId = requestAnimationFrame(this._tick);因此_requestIfNeeded的條件語句不會執行。
最後讓咱們來看看remove函數都幹了什麼:
簡而言之就是刪除fn,而這些邏輯都是由TickerListener完成的。那麼下一節咱們就來看看TickerListener是如何工做的。