mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-29 02:51:20 -08:00
docs(site): [SITE-04] document Iframe and Thumbnail tools
This commit is contained in:
1 parent
3aad294aa5
commit
484a8c903c
111 files changed
+5088
-2141
No files matched your search
@@ -0,0 +1,153 @@
|
||||
# Iframe 通信工具
|
||||
|
||||
[English](../en/tool/iframe.md)
|
||||
|
||||
在父页面控制 iframe 内的播放器,并把子页面事件传回父页面。它是独立构造器,不是放进 ArtPlayer plugins 数组的插件。本页描述当前未发布分支;父子页面都需要自行部署。
|
||||
|
||||
## 安装与双端接入
|
||||
|
||||
```sh
|
||||
yarn add artplayer-tool-iframe
|
||||
```
|
||||
|
||||
父页面使用默认导入 `import ArtplayerToolIframe from 'artplayer-tool-iframe'`。script 使用 `dist/artplayer-tool-iframe.js`,全局名为 `ArtplayerToolIframe`。
|
||||
|
||||
子页面也必须加载此工具并主动调用 inject;如果需要创建播放器,还要在子页面加载 ArtPlayer 和提供容器。例如仓库的 `/iframe.html` 使用:
|
||||
|
||||
```html
|
||||
<div class="artplayer-app" style="width:100%;height:100%"></div>
|
||||
<script src="./uncompiled/artplayer/index.js"></script>
|
||||
<script src="./uncompiled/artplayer-tool-iframe/index.js"></script>
|
||||
<script>ArtplayerToolIframe.inject();</script>
|
||||
```
|
||||
|
||||
以上路径属于本站开发输出,部署应用时替换成实际构建路径。下面保留[父页面原始示例](https://artplayer.org/?libs=./uncompiled/artplayer-tool-iframe/index.js&example=iframe),它创建 iframe 并指向 `/iframe.html`:
|
||||
|
||||
<div className="run-code" data-libs="./uncompiled/artplayer-tool-iframe/index.js">▶ Run Code</div>
|
||||
|
||||
```js
|
||||
// npm i artplayer-tool-iframe
|
||||
// import ArtplayerToolIframe from 'artplayer-tool-iframe';
|
||||
|
||||
const $iframe = document.createElement('iframe')
|
||||
$iframe.allowFullscreen = true
|
||||
$iframe.width = '100%'
|
||||
$iframe.height = '100%'
|
||||
|
||||
const $container = document.querySelector('.artplayer-app')
|
||||
$container.innerHTML = ''
|
||||
$container.appendChild($iframe)
|
||||
|
||||
const iframe = new ArtplayerToolIframe({
|
||||
iframe: $iframe,
|
||||
url: '/iframe.html',
|
||||
})
|
||||
|
||||
window.addEventListener('artplayer:example:cleanup', () => {
|
||||
iframe.destroy()
|
||||
$iframe.remove()
|
||||
}, { once: true })
|
||||
|
||||
iframe.message(({ type, data }) => {
|
||||
switch (type) {
|
||||
case 'fullscreenWeb':
|
||||
if (data) {
|
||||
$iframe.classList.add('fullscreenWeb')
|
||||
}
|
||||
else {
|
||||
$iframe.classList.remove('fullscreenWeb')
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
iframe.commit(() => {
|
||||
const art = new Artplayer({
|
||||
container: '.artplayer-app',
|
||||
url: '/assets/sample/video.mp4',
|
||||
fullscreen: true,
|
||||
fullscreenWeb: true,
|
||||
})
|
||||
|
||||
art.on('fullscreenWeb', (state) => {
|
||||
ArtplayerToolIframe.postMessage({
|
||||
type: 'fullscreenWeb',
|
||||
data: state,
|
||||
})
|
||||
})
|
||||
}).catch((error) => {
|
||||
if (!iframe.destroyed)
|
||||
console.error(error)
|
||||
})
|
||||
```
|
||||
|
||||
## 父页面实例
|
||||
|
||||
构造参数为 `{ iframe: HTMLIFrameElement, url: string }`,两项必填。构造时设置 iframe.src 并注册消息监听器;不会自动安装子页面脚本。iframe 元素由调用方创建和移除。
|
||||
|
||||
| 成员 | 行为 |
|
||||
| --- | --- |
|
||||
| `commit(callback)` | 提取函数体字符串,在子页面执行;返回响应 Promise |
|
||||
| `postMessage({ type, data, id? })` | 向子页面发送请求并等待响应;发送时分配自己的数值 id,忽略传入 id |
|
||||
| `message(callback)` | 设置一个消息通知回调,后一次覆盖前一次;同步返回 void |
|
||||
| `onMessage(event)` | 父页面接收器;通常由工具监听原生 message 事件 |
|
||||
| `destroy()` | 同步、幂等;移除本实例监听和导航观察,拒绝已发送及等待中的请求 |
|
||||
| `url` / `$iframe` | 初始配置地址及 iframe 元素;修改 url 字段本身不等于导航 |
|
||||
| `injected` / `destroyed` | 注入及销毁状态 |
|
||||
| `promises` | 待处理请求对象;历史回调字段拼写为 `resove` 和 `reject` |
|
||||
| `messageCallback` | 当前通知回调;默认函数,运行时也可为 null |
|
||||
|
||||
通知只接收 `{ type, data }`,this 为该工具实例,不带 id 或私有文档标记。匹配请求 id 的非 error 消息会正常结算;error 响应转换为 Error 拒绝。自定义消息类型仍可使用,但子页面需实现对应响应;未响应的请求没有默认超时。
|
||||
|
||||
注入前请求每 200ms 检查一次,不在 inject 事件里同步清空队列。数值 id 是关联编号,不保证等于 Date.now。销毁时未完成请求以 `The instance has been destroyed` 拒绝;调用方应处理 Promise 失败。
|
||||
|
||||
## commit 的执行方式
|
||||
|
||||
普通函数体可 return 可结构化克隆的结果。异步结果使用历史的字面量 `resolve(...)` 约定:
|
||||
|
||||
```js
|
||||
const title = await iframe.commit(() => {
|
||||
return document.title; // 在子页面读取
|
||||
});
|
||||
const answer = await iframe.commit((resolve) => {
|
||||
setTimeout(() => resolve(42), 100);
|
||||
});
|
||||
```
|
||||
|
||||
函数体通过字符串截取和 new Function 执行,不传递闭包、父页面局部变量、函数参数或 import。保留花括号;不要使用表达式箭头函数、async 函数体、顶层 await,或重命名异步 resolve 参数。该约定使用文本匹配,并非完整 JavaScript 语法分析。返回值需适合 postMessage,Promise 对象本身不能当作普通返回值发送。执行错误会发送 error 响应,子页面接收器也会拒绝。
|
||||
|
||||
## 子页面静态方法与信任边界
|
||||
|
||||
| 成员 | 行为 |
|
||||
| --- | --- |
|
||||
| `ArtplayerToolIframe.iframe` | 只读运行时 getter:当前页面是否位于 iframe 内 |
|
||||
| `inject()` | 发送注入通知并安装子页面接收器;重复调用保留单份监听 |
|
||||
| `postMessage({ type, data, id? })` | 向 parent 发送通知/响应,默认 id 为 0,返回 void |
|
||||
| `onMessage(event)` | 实际为 async 接收器;处理 commit,普通自定义类型需应用处理 |
|
||||
|
||||
后三项只能在 iframe 中使用;顶层调用抛错或拒绝。两端检查所选窗口与基本消息形状,继续使用 wildcard targetOrigin,未将初始 URL 的 origin 固定为白名单。父子页面内容必须可信;commit 会执行代码,需要允许对应执行方式的 CSP,它不是隔离沙箱或消息数据验证器。
|
||||
|
||||
## 导航、清理和兼容类型
|
||||
|
||||
双方升级后会协商文档标记,用于拒绝旧文档响应和取消确认离开页面的请求;标记不是认证凭证。替换 src 暂停发送,确认跨文档离开后取消旧请求;匹配的同文档 hash 变化保留请求。新页面排队请求与旧页面请求区分处理。旧子页面缺少文档标记时,同地址 reload 或内部导航的识别有局限,不能视为已验证 BFCache 恢复。
|
||||
|
||||
destroy 不移除 iframe、不销毁其播放器,也不新增静态子页面 destroy API。父页面可在释放工具后自行移除 iframe。独立页面的播放器事件、全屏权限及设备行为需要实际集成测试。
|
||||
|
||||
根和 `/legacy` 保留旧类声明,包括必填 Message.data、只读实例字段、同步静态 onMessage 和 `commit` 的 ReturnType 推导。准确运行时视图从同一入口按类型导入;没有 `/runtime` 子路径:
|
||||
|
||||
```ts
|
||||
import Iframe from 'artplayer-tool-iframe';
|
||||
import type { ResolverInstance, RuntimeConstructor } from 'artplayer-tool-iframe';
|
||||
|
||||
function connectFrame(element: HTMLIFrameElement) {
|
||||
const Runtime = Iframe as RuntimeConstructor;
|
||||
const tool = new Runtime({ iframe: element, url: '/iframe.html' }) as ResolverInstance;
|
||||
tool.message(function (message) { console.log(this.url, message.type, message.data); });
|
||||
const answer = tool.commit<number>((resolve) => { resolve(42); });
|
||||
return { tool, answer };
|
||||
}
|
||||
```
|
||||
|
||||
公开类型包括 Option、Message、Callbacks、Notification、MessageCallback、OutboundMessage、ProtocolMessage、Resolve、ResolverCallback、RuntimeInstance、ResolverInstance、RuntimeConstructor。类型视图不验证响应数据,也不改变执行协议。现代 CommonJS TS 可用 `import Iframe = require('artplayer-tool-iframe')`;ESM 使用默认导入。构造器没有 `.default` 自引用。旧 `artplayer-plugin-iframe` 的包名、命名空间和独立 helper 不等同于本工具,不能仅换依赖名就认为所有入口兼容。
|
||||
@@ -0,0 +1,181 @@
|
||||
# 本地视频缩略图工具
|
||||
|
||||
[English](../en/tool/thumbnail.md)
|
||||
|
||||
从用户选择的本地视频生成 PNG 缩略图拼图,可下载或交给播放器的 thumbnails 配置使用。这是独立构造器,不是 Auto Thumbnail 插件。本页描述未发布分支及已经批准的两种兼容模式。
|
||||
|
||||
## 安装和示例
|
||||
|
||||
```sh
|
||||
yarn add artplayer-tool-thumbnail
|
||||
```
|
||||
|
||||
ESM 使用 `import ArtplayerToolThumbnail from 'artplayer-tool-thumbnail'`。script 使用 `dist/artplayer-tool-thumbnail.js`,全局为 `ArtplayerToolThumbnail`。工具自身不依赖播放器;下面的[原始示例](https://artplayer.org/?libs=./uncompiled/artplayer-tool-thumbnail/index.js&example=tool.thumbnail)使用本站 DOM 和 ArtPlayer 展示结果:
|
||||
|
||||
<div className="run-code" data-libs="./uncompiled/artplayer-tool-thumbnail/index.js">▶ Run Code</div>
|
||||
|
||||
```js
|
||||
if (window.lastThumbnail) {
|
||||
window.lastThumbnail.destroy();
|
||||
}
|
||||
|
||||
var $popups = document.querySelector('.popups');
|
||||
var $popinner = document.querySelector('.popinner');
|
||||
var $artplayer = document.querySelector('.artplayer-app');
|
||||
|
||||
$artplayer.innerHTML = 'Drop video file here or click to upload.';
|
||||
var thumbnail = new ArtplayerToolThumbnail({
|
||||
fileInput: $artplayer,
|
||||
number: 60, // 数量
|
||||
width: 160, // 宽度
|
||||
column: 10, // 列数
|
||||
begin: 0, // 开始
|
||||
end: NaN, // 结束
|
||||
});
|
||||
|
||||
window.lastThumbnail = thumbnail;
|
||||
|
||||
thumbnail.on('file', function (file) {
|
||||
console.log('Read video successfully: ' + file.name);
|
||||
});
|
||||
|
||||
thumbnail.on('video', function (video) {
|
||||
console.log('Video size: ' + video.videoWidth + ' x ' + video.videoHeight);
|
||||
console.log('Video duration: ' + video.duration + 's');
|
||||
thumbnail.start();
|
||||
});
|
||||
|
||||
thumbnail.on('canvas', function (canvas) {
|
||||
console.log('Build canvas successfully');
|
||||
console.log('Canvas size: ' + canvas.width + ' x ' + canvas.height);
|
||||
console.log('Preview density: ' + thumbnail.density + ' p/s');
|
||||
});
|
||||
|
||||
thumbnail.on('update', function (url, percentage) {
|
||||
console.log('Processing: ' + Math.floor(percentage.toFixed(2) * 100) + '%');
|
||||
$popups.style.display = 'flex';
|
||||
$popinner.style.backgroundImage = 'url(' + url + ')';
|
||||
});
|
||||
|
||||
thumbnail.on('download', function (name) {
|
||||
console.log('Start download preview: ' + name);
|
||||
});
|
||||
|
||||
thumbnail.on('done', function () {
|
||||
$popups.style.display = 'none';
|
||||
thumbnail.download();
|
||||
console.log('Build preview image complete');
|
||||
|
||||
[...Artplayer.instances].forEach(function (art) {
|
||||
art.destroy(true);
|
||||
});
|
||||
|
||||
new Artplayer({
|
||||
container: $artplayer,
|
||||
url: thumbnail.videoUrl,
|
||||
autoSize: true,
|
||||
poster: thumbnail.thumbnailUrl,
|
||||
thumbnails: {
|
||||
url: thumbnail.thumbnailUrl,
|
||||
number: thumbnail.option.number,
|
||||
column: thumbnail.option.column,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('Build player complete');
|
||||
});
|
||||
```
|
||||
|
||||
选择文件只加载视频,不自动开始提取;示例在 video 事件里调用 start。应用代码应处理 start 的同步异常和 Promise 拒绝。video 通知不是 metadata 已就绪的保证,start 自己等待媒体信息。可在加载文件前注册监听,尤其是同步通知的工作区模式。
|
||||
|
||||
## 配置与默认行为
|
||||
|
||||
成功构造需要 `fileInput`:现有 `<input type="file">` 或用作上传区域的 Element。没有有效元素会同步抛错,即使类型中的构造参数可省略。包装元素会获得一个透明文件输入,原生 input 由调用方拥有。拖放和选择只读取第一个文件。
|
||||
|
||||
| 字段 | 默认值 | 含义 |
|
||||
| --- | --- | --- |
|
||||
| `fileInput` | 必须提供 | 文件输入或上传区域 |
|
||||
| `compatibility` | `published-3.5` 行为 | 也可显式选 `workspace-4.4` |
|
||||
| `number` | `60` | 截图数量,数值钳制到10–1000 |
|
||||
| `width` | `160` | 单张宽度,钳制到10–1000 |
|
||||
| `height` | `90` | 默认模式固定高度,钳制到10–1000 |
|
||||
| `column` | `10` | 列数,钳制到1–1000 |
|
||||
| `begin` | `0` | 片段起点,秒 |
|
||||
| `end` | `NaN` | 片段终点,秒;NaN/0使用媒体时长 |
|
||||
| `delay` | `300` | 默认模式的等待毫秒,钳制到10–1000 |
|
||||
|
||||
使用有限、有效的尺寸和整数数量/列数;历史数值检查不是整数校验器。start 会依据视频时长归一化区间,要求终点大于起点、时长有限且 `number / 区间秒数 <= 1`。例如默认60张需要至少60秒区间,不适合不修改参数就处理数秒视频。
|
||||
|
||||
| 行为 | 默认 / published-3.5 | workspace-4.4 |
|
||||
| --- | --- | --- |
|
||||
| 高度 | 保留配置 height | start 时按视频宽高比计算并写回 option.height |
|
||||
| video 事件 | 设置 src 后等待 delay | 设置 src 后同步通知 |
|
||||
| 截图等待 | 每次 seek 后至少经过 delay,并等待可用帧 | 等待可用帧,无固定额外延迟 |
|
||||
| done | 最后 update 后再等待 delay × 2 | 没有该固定等待 |
|
||||
| 文件 input 值 | 保留 | 读取文件后清空 |
|
||||
|
||||
依赖未发布4.4工作区行为的代码增加 `compatibility: 'workspace-4.4'`;delay 在该模式中被忽略。静态 `DEFAULTS` 每次返回一份默认配置对象,始终包含 published 模式的 delay,不随实例改变。实际定时器受浏览器调度影响,不保证精确毫秒。
|
||||
|
||||
## 方法、状态与输出
|
||||
|
||||
| 方法 | 行为 |
|
||||
| --- | --- |
|
||||
| `setup(options?)` | 合并部分配置、保留额外字段,返回 this;更换输入时迁移监听器 |
|
||||
| `loadVideo(file?)` | 接受 File;空值无操作;检查 canPlayType 后创建 Blob URL |
|
||||
| `start()` | 一次提取任务,返回 `Promise<void>`;重复任务或已就绪预检可能同步抛错 |
|
||||
| `creatScreenshotDate()` | 历史拼写;返回 `{ time, x, y }[]`,time 为秒 |
|
||||
| `creatCanvas()` | 历史拼写;创建带黑色背景及底部文字的拼图画布 |
|
||||
| `download()` | 完成且存在文件/图片后触发 PNG 下载,返回 this;未就绪抛错 |
|
||||
| `inputChange(event)` / `ondrop(event)` | 已绑定的输入处理器;通常由工具自己监听 |
|
||||
| `errorHandle(condition, message)` | 条件失败时发 error 并抛错 |
|
||||
| `destroy()` | 同步、幂等;取消任务并释放自有资源 |
|
||||
|
||||
静态 `creatVideo()` 创建并插入页面外的 muted/controls video;直接调用时由调用方管理该额外节点。静态 `ondragover(event)` 调用 preventDefault。这些历史名称没有改成 create*。
|
||||
|
||||
状态包括 processing、option、video、duration、density、file、videoUrl、thumbnailUrl,以及可选的事件注册表 e。duration 是选取区间长度,不一定是完整视频长度;density 为截图数/区间秒数。file/URL/density 在相应操作前可能不存在。processing 在 canvas 事件时仍为 false,在 update 时为 true,在 done 前恢复 false。
|
||||
|
||||
截图取各等分区间的中点:`begin + (i + 0.5) * duration / number`。拼图宽度为 width × column,高度为 ceil(number / column) × height + 30;底部30像素保留来源及布局文字。小数坐标沿用历史行为。逐张编码 PNG 并发 update,旧图片 Blob URL 被撤销;只保留最新 thumbnailUrl。下载名去掉原文件最后一段扩展名后加 `.png`,无扩展名文件沿用历史的 `.png` 名称。
|
||||
|
||||
## 事件和清理
|
||||
|
||||
on/once/emit/off 都返回 this;on/once 的第三个参数设置回调 this。off(name) 移除该事件全部监听,off(name, callback) 移除匹配监听。支持自定义字符串、数值和 symbol 事件;监听器异常会停止本次剩余回调,不会自动吞掉。
|
||||
|
||||
| 事件 | 参数与时机 |
|
||||
| --- | --- |
|
||||
| `file` | File;同步且早于 video.src 赋值 |
|
||||
| `video` | HTMLVideoElement;时序由模式决定 |
|
||||
| `canvas` | HTMLCanvasElement;开始提取前 |
|
||||
| `update` | 最新 URL、0–1进度 |
|
||||
| `done` | 无参数;先发事件,再结算 start Promise |
|
||||
| `download` | 下载文件名;触发链接点击后,不保证磁盘写入完成 |
|
||||
| `error` | 通常为消息字符串;用户回调异常可能带来其他值 |
|
||||
| `destroy` | 无参数;资源清理后发出一次 |
|
||||
|
||||
可以先 start 等待第一次文件选择;没有额外 metadata 超时。替换已有来源或 destroy 会取消旧任务,以 AbortError 拒绝且不为取消发 error。源加载、seek、绘制、编码或回调失败会结算任务;忽略过期回调,避免旧帧更新新结果。
|
||||
|
||||
destroy 移除自有 video、生成 input、监听器及 Blob URL,恢复仍由工具控制的包装元素 position;保留调用方 input 和事件注册表。清理会尽量全部执行,再抛出首个清理错误。已销毁实例不会重新创建输入或来源;start 返回取消拒绝。不要在依赖 videoUrl/thumbnailUrl 的播放器仍需要它们时销毁工具;URL 生命周期仍归工具所有。
|
||||
|
||||
文件 MIME 的 canPlayType、实际解码、Canvas 编码和 Blob URL 能力是不同条件。Windows WebKit 已有原生 Blob 加载缺口,不能把页面导航或 HTTP 视频加载成功当作本地截图成功。
|
||||
|
||||
## TypeScript
|
||||
|
||||
根和 `/legacy` 共享同一类类型,没有 `/runtime` 或运行时 `.default` 自引用。CommonJS TS 支持 `import Thumbnail = require('artplayer-tool-thumbnail')`,ESM 默认导入及类型导入如下:
|
||||
|
||||
```ts
|
||||
import Thumbnail, { type Option } from 'artplayer-tool-thumbnail';
|
||||
|
||||
function createTool(input: HTMLInputElement) {
|
||||
const options: Option = { fileInput: input, number: 10, height: 90 };
|
||||
const tool = new Thumbnail(options);
|
||||
tool.on('update', (url, progress) => console.log(url, progress));
|
||||
tool.on('video', () => {
|
||||
void (async () => {
|
||||
try { await tool.start(); }
|
||||
catch (error) { console.error(error); }
|
||||
})();
|
||||
});
|
||||
return tool;
|
||||
}
|
||||
```
|
||||
|
||||
类型包括 SheetOptions、Compatibility、DefaultOptions、Option、ResolvedOption、ScreenshotPoint、Events、EventArgs、Listener、EventRegistry。已知事件有精确参数;自定义协议仍需应用约定。旧工作区曾指向不存在的声明,当前类型不冒充已恢复的历史 TS 基线;完整旧 npm 归档缺口与回退验收仍独立记录。
|
||||
Reference in new issue
Block a user