PixiJS 源碼深度解讀:用于循環渲染的 Ticker 模塊
大家好,我是前端西瓜哥。這次來看看 PixiJS 的 Ticker 模塊源碼。
Ticker 的作用是 在下一幀繪制前調用監聽器,PixiJS 使用它來不斷對畫面進行重繪。
使用
在我們 實例化 PIXI.Application 時,PIXI.Application 內部注冊的插件之一的 TickerPlugin 會被初始化:
Application._plugins.forEach((plugin) => {
plugin.init.call(this, options);
});
將 Application 的渲染器 renderer 的 render 方法注冊進去,然后每幀都會會被調用。
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();
目錄結構
Ticker 的代碼都在 packages/ticker 目錄下。
構造函數
看一下 Ticker 的構造函數:
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)初始化一個空的監聽器函數
this._head = new TickerListener(null, null, Infinity);
使用了鏈表來管理多個監聽器函數,所以這個空的 this._head 就是這個鏈表的頭部 哨兵節點(虛節點)。
哨兵節點是一個鏈表編程技巧,目的是讓真正的頭部節點能和其他普通節點有一致的實現,不需要老是判斷 head 是否為 null,對頭節點做特殊處理,達到降低實現難度的目的。
監聽器函數會被封裝成一個 TickerListener 的類,提供 priority(優先級)、once(只執行一次) 等特性。其中的 next 指向下一個監聽函數,確保監聽器函數能夠按照注冊順序依次執行。
不過 TickerListener 有個 優先級的概念,監聽器在加入的時候,會插入合適的位置,最終保證優先級是從高往低的。優先級類型在 UPDATE_PRIORITY 這個枚舉變量中。
順帶一提,application 的 render 方法優先級是 UPDATE_PRIORITY.LOW,哨兵節點則是突破天際的 Infinity。
關于監聽器函數鏈表的維護相關代碼,其實就是雙向鏈表的一些常規操作,本文不會過多敘述。
(2)執行監聽器函數
this.update(time);
這里是執行所有監聽器函數的地方。
其下的核心代碼為:
while (listener)
{
listener = listener.emit(this.deltaTime);
}
listener.emit() 方法會執行當前的 listener 方法,并返回它的 next。不斷循環,直到 next 為 null 結束。
(3)循環調用 this._tick 方法
this._requestId = requestAnimationFrame(this._tick);
這里用了瀏覽器提供的 requestAnimationFrame 方法,實現循環調用 this._tick 方法。為了方便描述,通常簡寫為 raf。
this.head.next 表示有注冊的監聽函數。
結尾
Ticker 模塊并不復雜的,是對 requestAnimationFrame 的一層封裝,并使用鏈表做監聽器函數的維護。