点击下载教程相关资源
注意:本指南主要针对 Video.js 8+。它不再提及 videojs.extend() 方法的使用,该方法已在 8.0 中移除。有关详细信息,请参阅我们的迁移指南!
Video.js 播放器的架构以组件为中心。
播放器类以及代表播放器控件和其他 UI 元素的所有类都继承自 Component 类。通过这种架构,我们可以很容易地以树状结构构建 Video.js 播放器的用户界面,这种结构与 DOM 类似。
组件是一种 JavaScript 对象,具有以下特性:
几乎在所有情况下都与 DOM 元素相关联。
与播放器对象相关联。
能够管理任意数量的子组件。
能够监听和触发事件。
初始化和销毁的生命周期。
有关组件编程接口的更多细节,请参阅组件 API 文档。
一个组件的实例可以用 new Component() 创建。
// 创建按钮实例 var player = videojs('some-video-id'); var Button = videojs.getComponent('Button'); var button = new Button(player, { clickHandler: function(event) { videojs.log('Clicked'); } }); console.log(button.el());
上述代码将输出:
<button class="vjs-control vjs-button" type="button" aria-disabled="false"> <span class="vjs-icon-placeholder" aria-hidden="true"></span> <span class="vjs-control-text" aria-live="polite"></span> </button>
请注意,这虽然创建了按钮,但并未将其添加到 DOM 中。将其作为组件的子组件添加到 DOM 中。
player.getChild('ControlBar').addChild(button);
或者,通过向 addChild 传递一个组件的名称,就能在一个步骤中创建并添加该组件的新实例。
// 创建按钮实例并将其添加到播放器的控制栏中 var player = videojs('some-video-id'); var button = player.getChild('ControlBar').addChild('button', { clickHandler: function(event) { videojs.log('Clicked'); } }); console.log(button.el()); // 将产生与上例相同的 HTML 结果
按钮应有文字。默认按钮不显示文字,但 screenreaders 会显示文字,浏览器也会以工具提示的形式显示。按钮文本可以作为选项设置:
const myButton = player.getChild('ControlBar').addChild('button', { controlText: 'My button' });
要显示控件文本,请为按钮添加 vjs-visibile-text 类。
const myButton = player.getChild('ControlBar').addChild('button', { controlText: 'My button', className: 'vjs-visible-text' });
还可以在创建按钮后设置类和控制文本。
myButton.controlText('My button'); myButton.addClass('vjs-visible-text');
在播放器上启用 experimentalSvgIcons 选项后,可以使用 setIcon 方法在任何组件上添加或替换图标。
将 sandbox/svg-icons.html.example 重命名为 sandbox/svg-icons.html,使用 npm run build 构建 Video.js,然后在所选浏览器中打开 sandbox/svg-icons.html,即可查看所有可用图标。
myButton.setIcon('play');
Video.js 组件是 es6 类,可以使用类语法进行扩展,并在 Video.js 中注册,从而为播放器添加新功能和用户界面。
有几个函数用于创建和注册组件:
videojs.getComponent(String name): 从 Video.js 检索组件类。
videojs.registerComponent(String name, Function Comp):向 Video.js 注册组件类。
此示例创建了一个标题栏组件,作为 Component 的扩展类。
// 从 Video.js 获取组件基类 const Component = videojs.getComponent('Component'); class TitleBar extends Component { // 组件的构造函数会收到两个参数:将与之关联的播放器和一个选项对象。 constructor(player, options = {}) { // 重要的是要先调用超类,这样才能获得组件的所有功能! super(player, options); // 如果传入了 `text` 选项,则更新组件的文本内容。 if (options.text) { this.updateTextContent(options.text); } } // 组件的 `createEl` 函数创建其 DOM 元素。 createEl() { return videojs.dom.createEl('div', { // 在播放器中以 "vjs-"作为元素类的前缀是 Video.js 中使用的惯例。 className: 'vjs-title-bar' }); } // 该函数可随时调用,以更新组件的文本内容。 updateTextContent(text) { // 如果没有提供文本,则默认为 "Title Unknown"。 if (typeof text !== 'string') { text = 'Title Unknown'; } // 使用 Video.js 实用 DOM 方法来操作组件元素的内容。 videojs.emptyEl(this.el()); videojs.appendContent(this.el(), text); } }
请注意,由于 extend() 函数已被移除,在转译为 ES5 时,要扩展一个组件,需要使用以下语法。
// 我的 ES5 构造函数扩展了 Component function MyComponent(arguments) { Component.call(this, arguments); } MyComponent.prototype = Object.create(Component.prototype);
创建组件后,就可以在播放器中注册和使用。
// 在 Video.js 中注册该组件,以便在播放器中使用。 videojs.registerComponent('TitleBar', TitleBar); // 创建播放器 var player = videojs('my-player'); // 将 TitleBar 添加为播放器的后代,并在其选项中提供一些文本。 player.addChild('TitleBar', {text: 'The Title of The Video!'});
这个 JSBin 就是一个活生生的例子。
关于管理组件结构可用方法的完整细节,请再次参阅组件API 文档。
将子组件添加到父组件时,Video.js 会将子组件的元素插入父组件的元素中。例如,添加这样一个组件:
// 为播放器添加一个 "BigPlayButton" 组件。它的元素将追加到播放器的元素中。 player.addChild('BigPlayButton');
结果是一个 DOM,看起来像这样:
<!-- Player 元素 --> <div class="video-js"> <!-- BigPlayButton 元素 --> <div class="vjs-big-play-button"></div> </div>
相反,删除子组件会从 DOM 中删除子组件的元素:
player.removeChild('BigPlayButton');
结果是一个 DOM,看起来像这样:
<!-- Player 元素 --> <div class="video-js"> </div>
通过传入子构造函数的选项和 children 选项。
var player = videojs('some-vid-id'); var Component = videojs.getComponent('Component'); var myComponent = new Component(player); var myButton = myComponent.addChild('MyButton', { text: 'Press Me', buttonChildExample: { buttonChildOption: true } });
也可以在组件初始化时通过选项添加子组件。
注意:包含一个 "name" 键,如果两个相同类型的子组件需要不同的选项,则使用该键。
// MyComponent 源自上述示例 var myComp = new MyComponent(player, { children: ['button', { name: 'button', someOtherOption: true }, { name: 'button', someOtherOption: false }] });
var player = videojs('some-player-id'); var Component = videojs.getComponent('Component'); var myComponent = new Component(player); var myFunc = function() { var myComponent = this; console.log('myFunc called'); }; myComponent.on('eventType', myFunc); myComponent.trigger('eventType'); // logs 'myFunc called'
除非已绑定,否则 myFunc 的上下文将是 myComponent。您可以将监听器添加到另一个元素或组件中。
var otherComponent = new Component(player); // myComponent/myFunc is from the above example myComponent.on(otherComponent.el(), 'eventName', myFunc); myComponent.on(otherComponent, 'eventName', myFunc); otherComponent.trigger('eventName'); // logs 'myFunc called' twice
var player = videojs('some-player-id'); var Component = videojs.getComponent('Component'); var myComponent = new Component(player); var myFunc = function() { var myComponent = this; console.log('myFunc called'); }; myComponent.on('eventType', myFunc); myComponent.trigger('eventType'); // logs 'myFunc called' myComponent.off('eventType', myFunc); myComponent.trigger('eventType'); // does nothing
如果 myFunc 被排除,该事件类型的所有监听器都将被移除。如果 eventType 被排除,组件中的所有监听器都将被移除。您可以使用 off 功能移除添加到其他元素或组件的监听器:
myComponent.on(otherComponent...
在这种情况下,事件类型和监听器功能都是必须的。
var otherComponent = new Component(player); // myComponent/myFunc 来自上述示例 myComponent.on(otherComponent.el(), 'eventName', myFunc); myComponent.on(otherComponent, 'eventName', myFunc); otherComponent.trigger('eventName'); // logs 'myFunc called' twice myComponent.off(otherComponent.el(), 'eventName', myFunc); myComponent.off(otherComponent, 'eventName', myFunc); otherComponent.trigger('eventName'); // does nothing
var player = videojs('some-player-id'); var Component = videojs.getComponent('Component'); var myComponent = new Component(player); var myFunc = function() { var myComponent = this; console.log('myFunc called'); }; myComponent.one('eventName', myFunc); myComponent.trigger('eventName'); // logs 'myFunc called' myComponent.trigger('eventName'); // does nothing
您还可以在另一个元素或组件中添加监听器,只触发一次。
var otherComponent = new Component(player); // myComponent/myFunc 来自上述示例 myComponent.one(otherComponent.el(), 'eventName', myFunc); myComponent.one(otherComponent, 'eventName', myFunc); otherComponent.trigger('eventName'); // logs 'myFunc called' twice otherComponent.trigger('eventName'); // does nothing
var player = videojs('some-player-id'); var Component = videojs.getComponent('Component'); var myComponent = new Component(player); var myFunc = function(data) { var myComponent = this; console.log('myFunc called'); console.log(data); }; myComponent.one('eventName', myFunc); myComponent.trigger('eventName'); // logs 'myFunc called' and 'undefined' myComponent.trigger({'type':'eventName'}); // logs 'myFunc called' and 'undefined' myComponent.trigger('eventName', {data: 'some data'}); // logs 'myFunc called' and "{data: 'some data'}" myComponent.trigger({'type':'eventName'}, {data: 'some data'}); // logs 'myFunc called' and "{data: 'some data'}"
Video.js 播放器的默认组件结构如下所示:
Player ├── MediaLoader (has no DOM element) ├── PosterImage ├── TextTrackDisplay ├── LoadingSpinner ├── BigPlayButton ├── LiveTracker (has no DOM element) ├─┬ ControlBar │ ├── PlayToggle │ ├── VolumePanel │ ├── CurrentTimeDisplay (hidden by default) │ ├── TimeDivider (hidden by default) │ ├── DurationDisplay (hidden by default) │ ├─┬ ProgressControl (hidden during live playback, except when liveui: true) │ │ └─┬ SeekBar │ │ ├── LoadProgressBar │ │ ├── MouseTimeDisplay │ │ └── PlayProgressBar │ ├── LiveDisplay (hidden during VOD playback) │ ├── SeekToLive (hidden during VOD playback) │ ├── RemainingTimeDisplay │ ├── CustomControlSpacer (has no UI) │ ├── PlaybackRateMenuButton (hidden, unless playback tech supports rate changes) │ ├── ChaptersButton (hidden, unless there are relevant tracks) │ ├── DescriptionsButton (hidden, unless there are relevant tracks) │ ├── SubsCapsButton (hidden, unless there are relevant tracks) │ ├── AudioTrackButton (hidden, unless there are relevant tracks) │ ├── PictureInPictureToggle │ └── FullscreenToggle ├── ErrorDisplay (hidden, until there is an error) ├── TextTrackSettings └── ResizeManager (hidden)
PlayToggle 有一个重放选项,可以显示或隐藏重放图标。可通过 {replay: false} 设置该选项,因为默认情况下重放图标会在视频播放结束后显示。
隐藏重放图标示例:
let player = videojs('myplayer', { controlBar: { playToggle: { replay: false } } });
VolumePanel 包括 MuteToggle 和 VolumeControl 组件,如果不支持音量更改,这些组件将被隐藏。VolumePanel 有一个重要选项,可以使音量控制垂直显示在静音切换器上。可通过 VolumePanel {inline: false} 设置该选项,因为默认情况下音量控制是水平的,且带有 {inline: true}。
垂直音量控制示例:
let player = videojs('myplayer', { controlBar: { volumePanel: { inline: false } } });
文本轨道设置组件仅在使用模拟文本轨道时可用。
这个新组件负责在玩家大小发生变化时触发玩家调整大小事件。如果 ResizeObserver 可用或提供了多重填充,它就会使用 ResizeObserver。使用 ResizeObserver 时,它没有任何元素。如果 ResizeObserver 不可用,它就会回退到 iframe 元素,并通过去掉处理程序监听其调整大小事件。
ResizeObserver 填充程序可以这样传递:
var player = videojs('myplayer', { resizeManager: { ResizeObserver: ResizeObserverPoylfill } });
要强制使用 iframe 回退,请将 null 作为 ResizeObserver 传入:
var player = videojs('myplayer', { resizeManager: { ResizeObserver: null } });
也可以像这样禁用 ResizeManager:
var player = videojs('myplayer', { resizeManager: false });