Video.js Plugins
Video.js的一大優勢在於其插件生態系統,允許來自世界各地的作者分享他們的視頻播放器定制。這包括從最簡單的UI調整到新的播放技術和資源處理程序的一切!
因為我們將插件視為Video.js的重要組成部分,所以該組織致力於維護一套強大的插件作者工具:
-
generator-videojs-plugin
一個Video.js插件項目的Yeoman腳手架生成器。此外,它提供了一組插件作者的約定,如果遵循,使作者,貢獻和使用一致和可預測。簡而言之,生成器使插件作者專注於編寫他們的插件 - 不會弄亂工具。
-
videojs-spellbook
從版本3開始,插件生成器包括一個新的依賴關系:videojs-spellbook。 Spellbook是一個插件開發工具:它構建插件,創建標簽,運行開發服務器等。
Spellbook的好處是,您可以運行生成器一次,並在Spellbook中接收更新和錯誤修復,而無需再次運行生成器並處理Yeoman沖突和其他頭痛。
只要您的插件項目遵循約定,Spellbook應該可以工作!
Writing a Basic Plugin
如果以前寫過一個Video.js插件,基本的插件概念應該是熟悉的。 它類似於jQuery插件,其核心思想是您正在向播放器添加一種方法。
寫一個JavaScript函數
一個基本的插件是一個純粹的JavaScript函數:
function examplePlugin(options) { if (options.customClass) { this.addClass(options.customClass); } this.on('playing', function() { videojs.log('playback began!'); }); }
按照慣例,插件會傳遞一個選項對象; 然而,事實上你可以接受任何你想要的參數。 此示例插件將添加一個自定義類(無論以options.customClass的形式傳入),並且在播放開始時,它會將消息記錄到瀏覽器控制台。
注意:插件函數中的this值是播放器實例; 因此,您可以訪問其完整的API。
注冊一個基本插件
現在我們有一個與播放器有關的函數,剩下的就是用Video.js注冊插件:
videojs.registerPlugin('examplePlugin', examplePlugin);
之后,任何player將自動在其原型上擁有一個examplePlugin方法!
注意:插件名稱的唯一規定是它不能與任何現有的插件或播放器方法沖突。
編寫高級插件
Video.js 6引入了高級插件:這些插件與基本插件共享類似的API,但是基於類,並提供一系列額外的功能開箱即用。
在閱讀以下部分時,您可能需要參考Plugin API文檔以獲取更多詳細信息。
編寫一個JavaScript類/構造函數
如果您熟悉創建組件,則此過程是類似的。 一個高級插件從一個JavaScript類(a.k.a.一個構造函數)開始。
如果您已經使用ES6,您可以使用你選擇的解析器/語言的語法(Babel,TypeScript等):
const Plugin = videojs.getPlugin('plugin');
class ExamplePlugin extends Plugin {
constructor(player, options) {
super(player, options);
if (options.customClass) {
player.addClass(options.customClass);
}
player.on('playing', function() {
videojs.log('playback began!');
});
}
}
Or
with ES5:
var Plugin = videojs.getPlugin('plugin'); var ExamplePlugin = videojs.extend(Plugin, { constructor: function(player, options) { Plugin.call(this, player, options); if (options.customClass) { player.addClass(options.customClass); } player.on('playing', function() { videojs.log('playback began!'); }); } });
現在,這個例子的高級插件與上述基本插件完全相同 - 不用擔心,隨着我們的繼續,我們會使它更有趣!
注冊一個高級插件
高級插件的注冊過程與基本插件的過程相同。
videojs.registerPlugin('examplePlugin', ExamplePlugin);
注意:由於ES6類是JavaScript中現有構造函數和原型體系結構之上的語法糖,在所有情況下,registerPlugin的第二個參數是一個函數。
與基本插件的主要區別
高級插件與基本插件有兩個主要區別,在描述其高級功能之前,這些插件很重要。
The Value of this
使用基本的插件,這個插件函數的值將是播放器。
使用高級插件,this的值是插件類的實例。 播放器作為其第一個參數傳遞給插件構造函數(並自動作為播放器屬性應用於插件實例),並且之后傳遞任何其他參數。
The Player Plugin Name Property
通過調用播放器一個與插件匹配的名字的方法來設置基本插件和高級插件。(例如,player.examplePlugin())
However, with advanced plugins, this method acts like a factory function and it is replaced for the current player by a new function which returns the plugin instance:
然而,使用高級插件,此方法的作用就像工廠函數,並且通過返回插件實例的新函數替換當前播放器:
// `examplePlugin` has not been called, so it is a factory function. player.examplePlugin(); // `examplePlugin` is now a function that returns the same instance of // `ExamplePlugin` that was generated by the previous call. player.examplePlugin().someMethodName();
使用基本插件,該方法不會改變 - 它始終是相同的功能。 基本插件的作者可以處理對其插件功能的多次調用。
高級插件的特點
到目前為止,我們的示例高級插件在功能上與我們的示例基本插件相同。 然而,高級插件帶來了很多不是內置基本插件的好處。
Events
與組件一樣,高級插件提供事件的實現。 這包括:
-
能夠使用on或one來監聽插件實例上的事件:
player.examplePlugin().on('example-event', function() { videojs.log('example plugin received an example-event'); }); -
能夠在插件實例上觸發自定義事件:
player.examplePlugin().trigger('example-event'); -
能夠使用off停止監聽插件實例上的自定義事件:player.examplePlugin().off('example-event');
通過提供內置的事件系統,高級插件為大多數Web開發人員熟悉的模式提供了更廣泛的代碼結構選項。
Statefulness
為高級插件引入的新概念是statefulness。 這類似於React組件的state屬性和setState方法。
高級插件實例每個都有一個狀態屬性,它是一個純JavaScript對象 - 它可以包含插件作者想要的任何鍵和值。
可以通過向插件構造函數添加靜態屬性來提供默認狀態:
ExamplePlugin.defaultState = { customClass: 'default-custom-class' };
當狀態通過setState方法更新時,插件實例會觸發一個“statechanged”事件,但只有當事情發生變化時! 此事件可用作更新DOM或執行其他操作的信號。 傳遞給此事件的偵聽器的事件對象包括,一個描述發生在state屬性上的更改的對象:
player.examplePlugin().on('statechanged', function(e) {
if (e.changes && e.changes.customClass) {
this.player
.removeClass(e.changes.customClass.from)
.addClass(e.changes.customClass.to);
}
});
player.examplePlugin().setState({customClass: 'another-custom-class'});
Lifecycle
與組件相似,高級插件具有生命周期。 它們可以使用其工函數創建,並且可以使用其dispose法進行銷毀:
// set up a example plugin instance player.examplePlugin(); // dispose of it anytime thereafter player.examplePlugin().dispose();
dispose方法有幾個作用:
- 觸發插件實例上的“dispose”事件。
- 清理插件實例上的所有事件偵聽器,這有助於避免在清除對象后觸發事件引起的錯誤。
- 刪除插件狀態和播放器的引用以避免內存泄漏。
- 將播放器的命名屬性(例如player.examplePlugin)恢復回原始工廠功能,因此可以重新設置插件。
此外,如果播放器被銷毀,所有高級插件實例的銷毀也將被觸發。
高級示例高級插件
接下來是一個完整的ES6高級插件,當播放器的狀態在播放和暫停之間改變時,會記錄自定義消息。 它使用所有描述的高級功能:
import videojs from 'video.js'; const Plugin = videojs.getPlugin('plugin'); class Advanced extends Plugin { constructor(player, options) { super(player, options); // Whenever the player emits a playing or paused event, we update the // state if necessary. this.on(player, ['playing', 'paused'], this.updateState); this.on('statechanged', this.logState); } dispose() { super.dispose(); videojs.log('the advanced plugin is being disposed'); } updateState() { this.setState({playing: !player.paused()}); } logState(changed) { videojs.log(`the player is now ${this.state.playing ? 'playing' : 'paused'}`); } } videojs.registerPlugin('advanced', Advanced); const player = videojs('example-player'); player.advanced(); // This will begin playback, which will trigger a "playing" event, which will // update the state of the plugin, which will cause a message to be logged. player.play(); // This will pause playback, which will trigger a "paused" event, which will // update the state of the plugin, which will cause a message to be logged. player.pause(); player.advanced().dispose(); // This will begin playback, but the plugin has been disposed, so it will not // log any messages. player.play();
這個例子在現實中可能有點毫無意義,但它表明了高級插件在基本插件上提供的靈活性。
設置一個插件
在播放器上設置(或初始化)插件有兩種方法。 這兩種方式對於基本和高級插件都是相同的。
第一種方式是創建播放器期間。 使用插件選項,可以在播放器上自動設置插件:
videojs('example-player', {
plugins: {
examplePlugin: {
customClass: 'example-class'
}
}
});
另外,可以手動設置插件:
var player = videojs('example-player'); player.examplePlugin({customClass: 'example-class'});
這兩種方法在功能上是相同的 - 使用你喜歡的任何一種!
