Video.js 指南

这些指南涵盖了 Video.js 用户的各种主题

Video.js 插件

注意: 本指南侧重于 Video.js 8+。它不再提及使用 videojs.extend() 方法,该方法已在 8.0 版本中移除。请参阅我们的迁移指南以获取更多信息!

Video.js 的强大优势之一是其插件生态系统,它允许世界各地的作者分享他们的视频播放器定制。这包括从最简单的 UI 调整到新的播放技术和源处理程序的一切!

因为我们将插件视为 Video.js 的重要组成部分,所以该组织致力于维护一套强大的插件编写工具。

  • generator-videojs-plugin

    一个用于构建 Video.js 插件项目的 Yeoman 生成器。此外,它还提供了一套插件编写约定,如果遵循这些约定,可以使插件的编写、贡献和使用保持一致和可预测。

    简而言之,该生成器使插件作者能够专注于编写他们的插件,而不是纠结于工具。

编写基本插件

如果您以前编写过 Video.js 插件,那么基本插件的概念应该很熟悉。它类似于 jQuery 插件,其核心思想是为播放器添加一个方法。

编写 JavaScript 函数

基本插件是一个普通的 JavaScript 函数

function examplePlugin(options) {

  if (options.customClass) {
    this.addClass(options.customClass);
  }

  this.on('playing', function() {
    videojs.log('playback began!');
  });
}

按照约定,插件会传入一个 options 对象;但是,实际上您可以接受任何您想要的参数。这个示例插件将添加一个自定义类(通过 options.customClass 传入),并且每当播放开始时,它都会向浏览器控制台记录一条消息。

注意: 插件函数中 this 的值是播放器实例;因此,您可以访问其完整的 API

注册基本插件

现在我们有了一个对播放器执行操作的函数,剩下的就是向 Video.js 注册这个插件。

videojs.registerPlugin('examplePlugin', examplePlugin);

在此之后,任何播放器都将自动在其原型上拥有一个 examplePlugin 方法!

注意: 对插件名称的唯一要求是它不能与任何现有插件或播放器方法冲突。

编写高级插件

Video.js 6 引入了高级插件:这些插件与基本插件共享相似的 API,但是基于类,并开箱即用地提供了一系列额外功能。

在阅读以下部分时,您可能需要查阅插件 API 文档以获取更多详细信息。

编写 JavaScript 类/构造函数

如果您熟悉创建组件,这个过程是相似的。高级插件从一个 JavaScript 类开始

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!');
    });
  }
}

目前,这个示例高级插件的功能与上面描述的基本插件完全相同——别担心,我们将在后续使其变得更有趣!

注册高级插件

高级插件的注册过程与基本插件的注册过程相同。

videojs.registerPlugin('examplePlugin', ExamplePlugin);

与基本插件的主要区别

高级插件与基本插件有两个主要区别,在描述其高级特性之前,了解这些区别很重要。

`this` 的值

对于基本插件,插件函数中 this 的值将是播放器

对于高级插件,this 的值是插件类的实例。播放器作为第一个参数传递给插件构造函数(并自动作为 player 属性应用于插件实例),任何后续参数在此之后传递。

播放器插件名称属性

基本插件和高级插件都是通过在播放器上调用一个与插件名称匹配的方法(例如,player.examplePlugin())来设置的。

然而,对于高级插件,这个方法像一个工厂函数,并且它被一个返回插件实例的新函数替换,用于当前播放器。

// `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();

对于基本插件,该方法不会改变——它始终是相同的函数。基本插件的作者需要自行处理对其插件函数的多次调用。

高级插件的特性

到目前为止,我们的示例高级插件在功能上与我们的示例基本插件完全相同。然而,高级插件带来了基本插件所不具备的巨大优势。

事件

与组件类似,高级插件提供了事件的实现。这包括:

  • 使用 onone 监听插件实例上的事件的能力

    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 开发人员所熟悉。

额外事件数据

所有由插件触发的事件都包含一个额外的数据对象作为第二个参数。此对象有三个属性:

  • name:插件的名称(例如 "examplePlugin"),作为字符串。
  • plugin:插件构造函数(例如 ExamplePlugin)。
  • instance:插件构造函数实例。

状态管理

为高级插件引入的一个新概念是状态管理。这类似于 React 组件的 state 属性和 setState 方法。

每个高级插件实例都有一个 state 属性,它是一个普通的 JavaScript 对象——它可以包含插件作者想要的任何键和值。

可以通过向插件构造函数添加静态属性来提供默认的 state

ExamplePlugin.defaultState = {
  customClass: 'default-custom-class'
};

state 通过 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'});

生命周期

与组件类似,高级插件也有生命周期。它们可以通过其工厂函数创建,也可以使用其 dispose 方法销毁。

// set up a example plugin instance
player.examplePlugin();

// dispose of it anytime thereafter
player.examplePlugin().dispose();

dispose 方法有以下几个效果:

  • 在插件实例上触发一个 "dispose" 事件。
  • 清理插件实例上的所有事件监听器,这有助于避免对象清理后触发事件导致的错误。
  • 移除插件状态和对播放器的引用,以避免内存泄漏。
  • 将播放器的命名属性(例如 player.examplePlugin恢复到原始工厂函数,以便可以再次设置插件。

此外,如果播放器被销毁,其所有高级插件实例的销毁也将被触发。

版本

通过在插件注册之前在其上定义一个 VERSION 属性,可以为插件添加版本号。

ExamplePlugin.VERSION = '1.0.1';

videojs.registerPlugin('examplePlugin', ExamplePlugin);

使用 videojs.getPluginVersion 检索它。

var version = videojs.getPluginVersion('examplePlugin');
console.log(version);  // 1.0.1

请注意,插件生成器已经为您处理了版本号的添加。

日志记录

默认情况下,每个高级插件实例都有自己的 log 属性,就像 videojsPlayer 实例一样。日志消息将以播放器的 ID 和插件的名称为前缀。

player.examplePlugin().log('hello world!');

以上将记录以下内容:

VIDEOJS: $PLAYER_ID: examplePlugin: hello world!

log 函数还将拥有默认 videojs.log 的所有方法/属性;例如 error()warn()level() 等。

注意: 此方法在构造函数中添加,并且不会覆盖插件原型中任何预定义的 log 属性。

高级示例高级插件

以下是一个完整的 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 pause event, we update the
    // state if necessary.
    this.on(player, ['playing', 'pause'], this.updateState);
    this.on('statechanged', this.logState);
  }

  dispose() {
    super.dispose();
    videojs.log('the advanced plugin is being disposed');
  }

  updateState() {
    this.setState({playing: !this.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();

这个例子在实际中可能有点无关紧要,但它展示了高级插件相对于基本插件所提供的灵活性。

设置插件

有两种方法可以在播放器上设置(或初始化)插件。这两种方法对于基本插件和高级插件都同样适用。

第一种方法是在播放器创建期间。使用 plugins 选项,可以在播放器上自动设置插件。

videojs('example-player', {
  plugins: {
    examplePlugin: {
      customClass: 'example-class'
    }
  }
});

或者,可以手动设置插件。

var player = videojs('example-player');
player.examplePlugin({customClass: 'example-class'});

这两种方法在功能上是相同的——请使用您喜欢的方式!

插件设置事件

有时,会出现一些代码需要等待插件初始化的情况。从 Video.js 6 开始,可以通过监听播放器上的 pluginsetup 事件来实现。

对于任何给定的插件初始化,有四个需要注意的事件:

  • beforepluginsetup:在任何插件初始化之前立即触发。
  • beforepluginsetup:examplePlugin:在 examplePlugin 初始化之前立即触发。
  • pluginsetup:在任何插件初始化之后触发。
  • pluginsetup:examplePlugin:在 examplePlugin 初始化之后触发。

这些事件适用于基本插件和高级插件。它们在播放器上触发,并且每个事件都包含一个额外事件数据对象作为其监听器的第二个参数。

参考资料