mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-11 05:26:15 -08:00
169 lines
6.3 KiB
Markdown
169 lines
6.3 KiB
Markdown
# 编写插件
|
|
|
|
了解播放器的`属性`、`方法`和`事件`后,就可以编写插件。
|
|
|
|
## 注册与生命周期 {#plugin-contract}
|
|
|
|
`plugins` 配置接收工厂函数。工厂被调用时,唯一参数和普通函数的 `this` 都是播放器;箭头函数使用自己的词法 `this`。带配置的插件通常先调用外层函数,得到真正的工厂,如 `plugins: [adsPlugin(options)]`。工厂直接返回的对象会成为注册结果,不会被复制。
|
|
|
|
同步工厂在 `add` 返回前完成注册,`art.plugins.add(factory)` 返回注册管理器本身。只有与播放器同一 JavaScript realm 的 `Promise` 才会被等待;此时返回的 Promise 最终兑现为管理器,而不是插件结果。普通 thenable 和其他窗口的 Promise 保持原样作为同步结果。需要等待异步插件时,应在构造之后 `await art.plugins.add(factory)`,然后读取对应名称。
|
|
|
|
构造配置中的工厂按数组顺序启动,但不会逐个等待异步工厂;播放器 `ready` 也不等待它们。构造时前面的工厂执行期间,`art.plugins` 尚未赋值,不应在工厂里依赖它访问前一个插件。同步工厂抛错会使构造或直接 `add` 失败;直接 `add` 的异步拒绝由调用者处理,构造配置中的异步拒绝会记录警告。
|
|
|
|
名称取第一个真值:结果的 `name`、工厂函数名、`plugin` 加当前注册计数。异步匿名工厂的回退名称使用完成时的计数,因此推荐显式返回稳定的字符串名称。`id` 在调用工厂前递增,失败也占用计数;内置插件也参与计数。`art` 指向宿主;`next(factory, result)` 是直接提交结果的底层方法,不调用工厂、不递增计数、不等待 Promise,通常无需手动调用。
|
|
|
|
结果以不可写、不可配置、不可枚举的自有属性保存,因此 `Object.keys(art.plugins)` 不会列出插件,重复名称会抛错。不要使用 `art`、`id`、`add`、`next` 等管理器成员名;名称与原型方法冲突可能遮蔽方法。管理器没有通用的移除、替换或自动调用插件 `destroy` 的机制。
|
|
|
|
插件应自行监听播放器 `destroy`,清理请求、定时器、Worker 和外部资源。异步工厂也要在首次等待之前注册清理逻辑,并在等待结束后检查是否已销毁。销毁后的迟到结果不会注册,也不会被自动清理;已经挂起的 `add` 仍返回其原 Promise,若工厂成功则兑现为管理器。销毁期间或之后的新 `add` 会在执行工厂前抛错。
|
|
|
|
## TypeScript {#plugin-types}
|
|
|
|
根入口保留历史 `Plugins.add` 的 Promise 返回声明;它不表示同步运行时变成异步。`artplayer/runtime` 的 `PluginRegistration` 按工厂返回类型区分同步管理器与 Promise;`unknown` 返回值保留二者联合。跨 realm 的 Promise 无法仅靠 TS 类型判定,仍遵守上面的运行时规则。
|
|
|
|
模块扩展只声明结果类型,不负责安装插件。准确入口的 `Plugins` 也承接根入口已有插件的命名扩展。构造选项的 `PluginFactory` 使用初始化阶段的 `PluginHost`,避免假定工厂执行时实例已完全构造;实例化之后的 `add` 可以使用完整播放器。
|
|
|
|
```ts
|
|
import Artplayer from 'artplayer/runtime';
|
|
import type { Plugins } from 'artplayer/runtime';
|
|
|
|
declare module 'artplayer/runtime' {
|
|
interface Plugins {
|
|
exampleCounter: { name: string; value: number };
|
|
}
|
|
}
|
|
|
|
const art = new Artplayer({ container: '.artplayer-app', url: '/assets/sample/video.mp4' });
|
|
const registry: Plugins = art.plugins.add(function (host) {
|
|
const samePlayer: boolean = this === host;
|
|
return { name: 'exampleCounter', value: samePlayer ? 1 : 0 };
|
|
});
|
|
const value: number = registry.exampleCounter.value;
|
|
const pending: Promise<Plugins> = art.plugins.add(async () => ({ name: 'exampleAsync' }));
|
|
void pending.catch(console.error);
|
|
void value;
|
|
```
|
|
|
|
## 示例
|
|
|
|
可以在实例化的时候加载插件的函数
|
|
|
|
<div className="run-code">▶ Run Code</div>
|
|
|
|
```js{15}
|
|
function myPlugin(art) {
|
|
console.info(art);
|
|
return {
|
|
name: 'myPlugin',
|
|
something: 'something',
|
|
doSomething: function () {
|
|
console.info('doSomething');
|
|
},
|
|
};
|
|
}
|
|
|
|
var art = new Artplayer({
|
|
container: '.artplayer-app',
|
|
url: '/assets/sample/video.mp4',
|
|
plugins: [myPlugin],
|
|
});
|
|
|
|
art.on('ready', () => {
|
|
console.info(art.plugins.myPlugin);
|
|
});
|
|
```
|
|
|
|
可以在实例化之后再加载插件的函数
|
|
|
|
<div className="run-code">▶ Run Code</div>
|
|
|
|
```js{17}
|
|
function myPlugin(art) {
|
|
console.info(art);
|
|
return {
|
|
name: 'myPlugin',
|
|
something: 'something',
|
|
doSomething: function () {
|
|
console.info('doSomething');
|
|
},
|
|
};
|
|
}
|
|
|
|
var art = new Artplayer({
|
|
container: '.artplayer-app',
|
|
url: '/assets/sample/video.mp4',
|
|
});
|
|
|
|
art.plugins.add(myPlugin);
|
|
|
|
art.on('ready', () => {
|
|
console.info(art.plugins.myPlugin);
|
|
});
|
|
```
|
|
|
|
例如我想写一个在视频暂停后,显示一个图片广告的插件
|
|
|
|
<div className="run-code">▶ Run Code</div>
|
|
|
|
```js
|
|
function adsPlugin(option) {
|
|
return (art) => {
|
|
art.layers.add({
|
|
name: 'ads',
|
|
html: `<img style="width: 100px" src="${option.url}">`,
|
|
style: {
|
|
display: 'none',
|
|
position: 'absolute',
|
|
top: '20px',
|
|
right: '20px',
|
|
},
|
|
});
|
|
|
|
function show() {
|
|
art.layers.ads.style.display = 'block';
|
|
}
|
|
|
|
function hide() {
|
|
art.layers.ads.style.display = 'none';
|
|
}
|
|
|
|
art.controls.add({
|
|
name: 'hide-ads',
|
|
position: 'right',
|
|
html: 'Hide Ads',
|
|
tooltip: 'Hide Ads',
|
|
click: hide,
|
|
style: {
|
|
marginRight: '20px'
|
|
}
|
|
});
|
|
|
|
art.controls.add({
|
|
name: 'show-ads',
|
|
position: 'right',
|
|
html: 'Show Ads',
|
|
tooltip: 'Show Ads',
|
|
click: show,
|
|
});
|
|
|
|
art.on('play', hide);
|
|
art.on('pause', show);
|
|
|
|
return {
|
|
name: 'adsPlugin',
|
|
show,
|
|
hide
|
|
};
|
|
}
|
|
}
|
|
|
|
var art = new Artplayer({
|
|
container: '.artplayer-app',
|
|
url: '/assets/sample/video.mp4',
|
|
plugins: [
|
|
adsPlugin({
|
|
url: '/assets/sample/layer.png'
|
|
})
|
|
],
|
|
});
|
|
```
|