PixiJS 源碼深度解讀:用于循環(huán)渲染的 Ticker 模塊

大家好,我是前端西瓜哥。這次來看看 PixiJS 的 Ticker 模塊源碼。
Ticker 的作用是 在下一幀繪制前調(diào)用監(jiān)聽器,PixiJS 使用它來不斷對畫面進行重繪。
使用
在我們 實例化 PIXI.Application 時,PIXI.Application 內(nèi)部注冊的插件之一的 TickerPlugin 會被初始化:
Application._plugins.forEach((plugin) => {
plugin.init.call(this, options);
});將 Application 的渲染器 renderer 的 render 方法注冊進去,然后每幀都會會被調(diào)用。
ticker = options.sharedTicker ? Ticker.shared : new Ticker();
ticker.add(this.render, this, UPDATE_PRIORITY.LOW);也可以直接使用:
const ticker = new PIXI.Ticker();
// ticker.autoStart = true;
ticker.add(() => {
console.log("前端西瓜哥");
});
ticker.add(() => {
console.log("fe_watermelon");
});
ticker.start();目錄結(jié)構(gòu)
Ticker 的代碼都在 packages/ticker 目錄下。

構(gòu)造函數(shù)
看一下 Ticker 的構(gòu)造函數(shù):
class Ticker {
constructor() {
this._head = new TickerListener(null, null, Infinity);
this.deltaMS = 1 / Ticker.targetFPMS;
this.elapsedMS = 1 / Ticker.targetFPMS;
this._tick = (time: number): void => {
this._requestId = null;
if (this.started) {
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
if (this.started && this._requestId === null && this._head.next) {
this._requestId = requestAnimationFrame(this._tick);
}
}
};
}
}做了哪些事情:
(1)初始化一個空的監(jiān)聽器函數(shù)
this._head = new TickerListener(null, null, Infinity);使用了鏈表來管理多個監(jiān)聽器函數(shù),所以這個空的 this._head 就是這個鏈表的頭部 哨兵節(jié)點(虛節(jié)點)。
哨兵節(jié)點是一個鏈表編程技巧,目的是讓真正的頭部節(jié)點能和其他普通節(jié)點有一致的實現(xiàn),不需要老是判斷 head 是否為 null,對頭節(jié)點做特殊處理,達到降低實現(xiàn)難度的目的。
監(jiān)聽器函數(shù)會被封裝成一個 TickerListener 的類,提供 priority(優(yōu)先級)、once(只執(zhí)行一次) 等特性。其中的 next 指向下一個監(jiān)聽函數(shù),確保監(jiān)聽器函數(shù)能夠按照注冊順序依次執(zhí)行。
不過 TickerListener 有個 優(yōu)先級的概念,監(jiān)聽器在加入的時候,會插入合適的位置,最終保證優(yōu)先級是從高往低的。優(yōu)先級類型在 UPDATE_PRIORITY 這個枚舉變量中。
順帶一提,application 的 render 方法優(yōu)先級是 UPDATE_PRIORITY.LOW,哨兵節(jié)點則是突破天際的 Infinity。
關(guān)于監(jiān)聽器函數(shù)鏈表的維護相關(guān)代碼,其實就是雙向鏈表的一些常規(guī)操作,本文不會過多敘述。
(2)執(zhí)行監(jiān)聽器函數(shù)
this.update(time);這里是執(zhí)行所有監(jiān)聽器函數(shù)的地方。
其下的核心代碼為:
while (listener)
{
listener = listener.emit(this.deltaTime);
}listener.emit() 方法會執(zhí)行當(dāng)前的 listener 方法,并返回它的 next。不斷循環(huán),直到 next 為 null 結(jié)束。
(3)循環(huán)調(diào)用 this._tick 方法
this._requestId = requestAnimationFrame(this._tick);這里用了瀏覽器提供的 requestAnimationFrame 方法,實現(xiàn)循環(huán)調(diào)用 this._tick 方法。為了方便描述,通常簡寫為 raf。
this.head.next 表示有注冊的監(jiān)聽函數(shù)。
結(jié)尾
Ticker 模塊并不復(fù)雜的,是對 requestAnimationFrame 的一層封裝,并使用鏈表做監(jiān)聽器函數(shù)的維護。






























