@mm-player/player-ui
v0.4.0
Published
播放器 UI(Renderer / Theme / Web Component / VideoMediaTech)
Maintainers
Readme
@mm-player/player-ui
mm-player UI 层:Renderer / Theme / Web Component / VideoMediaTech。依赖
@mm-player/player-core,不反向依赖。
模块用途
在 Headless 核心之上提供完整浏览器 UI:10 个独立 Renderer、CSS Variable 主题、<mm-player> Web Component(Shadow DOM 隔离)、控件/封面/Overlay/插件 API、浏览器在线/离线联动。VideoMediaTech 在此包内创建 video 元素并注入 Core。
适用场景
- 需要开箱即用的播放器界面。
- 需要声明式 Web Component(
<mm-player>)或 Shadow DOM 样式隔离。 - 需要运行时主题切换、Overlay、插件扩展。
如需纯逻辑核心(无 DOM),使用 @mm-player/player-core。
引入
pnpm add @mm-player/player-uiplayer-ui 内部创建 VideoMediaTech(含 <video>)并注入 PlayerCore,调用方无需手动注入。
文件与流的统一入口(自 0.4.0)
推荐 adapter: 'auto'(PlayerUI 构造时省略也等价)。MP4(含独立 fMP4)、WebM、MOV、OGV、blob: 单源经 FileAdapter 使用同一个 VideoMediaTech 的原生 video.src;m3u8/mpd 经 Shaka、flv/whep 经对应适配器。load() 只换源/适配器,保留同一 video、Core、控制栏、加载态、错误态、全屏及主题。
const player = new PlayerUI({
container: '#player', adapter: 'auto',
url: '/api/download?path=%2Fclip.mp4', mimeType: 'video/mp4',
isLive: false, muted: true, autoplay: false, bandwidthMode: 'high',
ui: { bottomBar: { progress: true, left: ['play', 'time'] } }
});
await player.play();
await player.load({ url: 'https://example.com/live.flv', isLive: true });
await player.play();
await player.load({ url: 'https://example.com/vod.mpd', isLive: false });
await player.play();
await player.load({ url: '/api/download?id=opaque', mimeType: 'video/mp4', isLive: false });
await player.play();自动判定只看显式 MIME、URL 路径扩展名或 path/filename/file 参数,不发类型探测请求。不透明文件可强制 adapter: 'file';不透明流须明确其适配器/清单 MIME。未知地址保留 WHEP 回退。空客户端列表且无新 URL 时沿用当前实际协议,reload 不重判不透明地址;后续新源仍自动识别。显式适配器会继续沿用,要恢复自动识别传 'auto'。新 URL 省略 MIME 清除旧值,reload 保留。isLive 不参与路由:文件 false,FLV 按实际传值,DASH/HLS/WHEP 按实际媒体状态显示直播。
文件模式不提供 ABR、音轨/字幕选择、播放列表或网络统计;提供播放、seek、倍速、PiP、截图,控件仍按能力驱动。外挂字幕沿用显式 Shaka 单源契约(文件模式显式非空 subtitles 早拒);原有 Shaka MP4 调用保持兼容并显式传媒体 MIME。blob: 清单请明确 Shaka/MIME。MOV/codec/fMP4 seek 能力由浏览器决定。0.3.0 / 聚合包 1.4.0 不含这些入口。
本版已知限制:Shaka → file 切源在部分时序中可能保留零倍速,当前可通过设置菜单/示例选择 1× 或 player.getCore().setPlaybackRate(1) 恢复。速率自动恢复及快照校准尚未修复;Edge/Firefox 完整播放验收未完成。
最小示例
Vanilla JS
import { PlayerUI } from '@mm-player/player-ui';
const player = new PlayerUI({
container: '#player',
url: 'https://example.com/stream.mpd',
adapter: 'shaka',
autoplay: false,
ui: {
bottomBar: { progress: true, left: ['play', 'time'], right: ['volume', 'fullscreen'] }
}
});
player.on('error', (err) => console.error(err));
await player.play();
// 运行时增量切换主题;颜色令牌使用 "R, G, B" 通道值
player.setTheme({
'--mm-theme-color': '255, 85, 0',
'--mm-accent-color': '255, 85, 0',
'--mm-border-radius': '8px'
});
console.log(player.getTheme()); // 返回合并后的完整 ThemeTokens
// 释放
await player.destroy();Web Component
<mm-player
src="https://example.com/stream.flv"
protocol="flv"
controls
autoplay
muted
></mm-player>
<script type="module">
import '@mm-player/player-ui'; // 注册 <mm-player> 自定义元素
const el = document.querySelector('mm-player');
el.addEventListener('error', (e) => console.error(e.detail));
el.addEventListener('timeupdate', (e) => console.log(e.detail));
</script>关键 API
PlayerUI
| 方法 | 说明 |
| --- | --- |
| constructor(options: PlayerUIOptions) | container 必填(元素/ShadowRoot/选择器) |
| load / reload | 加载 / 重新加载媒体;load(config) 为增量合并语义:先把 config 合并进实例配置,再把合并后完整实例配置的独立快照交给 Core(浅拷贝 + url、subtitles、playlist 数组及条目副本,与 switchAdapter/beforePlay 路径一致——调用方在返回前修改传入的配置对象或数组不影响已入队请求)。config 省略 adapter 时沿用实例的选择策略(自动模式每次重新判定,显式模式沿用适配器),显式传入时切换;url 与 playlist(WHEP/FLV 客户端列表)互斥(后写赢,显式提供其一即归一化退出另一形态),reload() 维持当前模式。配置在加载尝试前提交,失败保留最近一次请求配置、reload() 按其重试。并发调用仍由 Core 生命周期 FIFO 串行完成,但较早请求完成后若已被后续请求替代,不执行控件同步与自动播放(最终状态由最后一次请求收敛)。加载成功后按该次请求快照同步播放列表控件;加载失败按 Core 真实资源状态清理自动 prev/next(只删不注,见内置控制节),错误原样向调用方拒绝 |
客户端列表配置边界:UI 不会把同请求的非空 playlist + 多元素 url 数组静默归一为单一形态,也不会因同时传入 url 而吞掉非法的 playlist: null;两种请求均交由 Core 在释放旧资源前以 CONFIG_ERROR 拒绝。正常的单地址与客户端列表切换仍按上述增量合并规则执行。
仅调用 load({ playlist: [] }) 时,UI 与 Core 一起沿用当前曲目的 URL 和适配器;成功后 UI 实例配置也同步该 URL,随后 reload() 不会因进入列表时清空了 options.url 而失去媒体源。若要同时切换协议,须提供新 URL,不能把当前曲目地址隐式交给另一适配器。
| play / stop | 返回 Promise<void> 的播放/停止控制(经 CommandBus);异步 beforePlay 的迟到结果受加载请求代次与播放意图代次保护,更晚的加载请求不会被旧配置覆盖,更晚的 stop 不会被旧请求反向恢复为播放 |
| pause | 同步暂停;低带宽模式会在后台调用 stop;两种模式都会同步撤销在途自动播放意图 |
| switchAdapter(config) | 运行时切换协议(whep/flv/shaka);加载期间发生更晚的 pause/stop/destroy 时仍完成换源与控件同步,但不会迟到自动播放 |
| setVolume / setMuted | 音量(0–1)/ 静音 |
| enterFullscreen / exitFullscreen / toggleFullscreen / isFullscreen | 全屏(经 CommandBus fullscreen 命令,与按钮/F 快捷键共享同一处理器;isFullscreen 仅在当前播放器根容器全屏时为 true;拒绝路径收敛为 console.warn,不产生未处理拒绝) |
| canPip / enterPip / exitPip / togglePip / isPipActive | 画中画(标准 Picture-in-Picture API;canPip 同时检查适配器能力、浏览器支持和 video.disablePictureInPicture;返回 Promise<boolean> 表示是否到达目标状态;状态与 pipchange 事件以 document.pictureInPictureElement 为唯一事实来源) |
| screenshot(options?) | 截图(同步返回 CaptureResult,失败抛错);内置截图按钮经 screenshot 命令派发,成功触发下载、失败输出可理解提示 |
| returnToLive | 回到直播沿(经 CommandBus seek 载荷 'live',与控制栏按钮共享链路;VOD/WHEP 为 no-op) |
| isOffline | 当前设备网络是否离线(去重后状态;构造时读 navigator.onLine,此后由 window online/offline 事件驱动,与 offlinechange 事件载荷同源) |
| getQualityTracks / getCurrentQuality / setAbrEnabled / isAbrEnabled / switchQuality | 清晰度 / ABR 类型安全代理(见 player-core README) |
| getAudioTracks / getCurrentAudioTrack / setAudioTrack | 音轨类型安全代理(见 player-core README;language:role 分组键载荷) |
| getSubtitleTracks / getCurrentSubtitleTrack / setSubtitleTrack | 字幕类型安全代理(见 player-core README;载荷为字幕轨 id 或 'off' 关闭) |
| next / prev / jumpTo | 播放列表切曲代理(Shaka 原生队列与 WHEP/FLV 客户端列表共用;切曲失败输出固定诊断 [PlayerUI] next failed 等,不产生未处理 rejection;到端/越界/未启动安全返回 false) |
| getStrings / setStrings | 文案字典读取(完整副本)/ 运行时增量合并(与 setTheme 同风格),已渲染标签同步刷新 |
| setCover / clearCover | 封面 |
| setControlsVisible | 控制栏显隐 |
| setTheme / getTheme | 运行时主题(不重建 Renderer) |
| showOverlay / hideOverlay / setOverlay / removeOverlay | 程序化 Overlay |
| addPlugin / removePlugin | 插件(CorePlugin/UIPlugin/Legacy) |
| exportSnapshot / restoreSnapshot | 快照导出/恢复 |
| healthCheck | 健康检查 |
| getDevTools | DevTools(惰性) |
| on / off / once | 事件(offlinechange 为 PlayerUI 本地事件、不依赖适配器;其余事件代理 Core;另含 pipchange、screenshotresult、screenshotfailed、qualitychanged、adaptation、trackschanged) |
| getCore | 获取 PlayerCore 实例 |
| getPlayerContainer | 获取播放器根容器(video 与渲染层宿主;Web Component 的尺寸监听即基于它) |
| moveTo(container) | 移动到新容器(保持进度) |
| destroy | 异步完整释放(含 DOM 命令注销与 PiP/全屏/截图/网络 online-offline 监听解绑) |
内置控制与设置菜单
默认截图按钮使用线框相机和独立圆形镜头,在小尺寸控制栏中保持可辨识;可通过 iconConfig.symbols.screenshot 提供自定义 symbol,仍优先于内置图标。
控制栏内置按钮由 Capability 驱动显隐(不判断协议字符串):fullscreen(浏览器能力)、pip(canPip,浏览器不支持标准 API 时可见但禁用)、screenshot(canScreenshot)、menu(由「实际存在可展示的菜单分区」决定:倍速 canChangeSpeed / 清晰度 supportsQuality 且有轨道 / 音轨 supportsAudioTrack 且分组 > 1 / 字幕 canSubtitle 且轨道 ≥ 1,任一成立即显示——只支持音轨或字幕的第三方适配器同样有入口;全部不成立时隐藏,WHEP 等场景不显示无项可选的死菜单;轨道列表变化后的 state-change 自动重判显隐)、live(「回到直播」文字按钮:快照 live 且延迟超过「会话内最佳延迟 + 3s」自适应基线时显示,点击经 seek 载荷 'live' 派发;VOD/WHEP 恒不出现)、prev/next(播放列表控件:playlist 能力位为 true 且当前配置为长度 > 1 的 url 数组(Shaka 原生队列)或非空 playlist 客户端列表(WHEP/FLV,Core 派生能力;构造器携带 playlist 配置同样触发初始加载)时自动注入——构造器初始 load 成功后即注入(autoplay: false 无需等待首次 play),并在 load/reload/switchAdapter/play 成功后按该次请求的配置快照同步,判重以真实控件注册表为准,首次载入、重复 load、reload、播放往复均不重复注册、不输出重复注册警告;并发时较早请求完成不提前注入/删除(最终由最后一次请求收敛);初始加载失败、单源或能力位关闭的适配器(未配置客户端列表的 WHEP/FLV,URL 误传数组也不注入)不注入。注入后的禁用态由快照 playlist 派生(2026-09-23 真实状态产品化):首曲禁 prev、末曲禁 next,loop 配置(与切曲回绕判定同源)下两端保持可用;快照无 playlist(列表/队列未启动或非列表源)时恢复可用,不出现死按钮;禁用只作用于 PlayerUI 工厂创建的按钮——ui 配置声明或 addControl() 添加的自定义 prev/next 永不被触碰。加载失败时的清理规则(只删不注):失败的加载请求绝不为它注入新控件;旧自动控件按 Core 真实资源状态决定去留——Core 仍声明 playlist 能力且已合并配置仍为多 URL 队列或非空客户端列表时保留(典型:非法 adapter 在销毁旧适配器前被拒绝、播放列表/客户端列表 reload 失败——Core 仍持有旧播放列表或失败请求本就是列表形态),否则移除(典型:列表切单源后 load 失败、动态适配器加载失败 Core 无适配器)。失败后再次加载成功时控件恢复正确状态。切离播放列表形态时仅移除由 PlayerUI 自动注入的控件——归属以显式所有权记录为准(只收录自动注入成功的控件 id),ui 配置声明的自定义 prev/next 与运行时 addControl() 动态添加的 prev/next 属调用方,任何同步与失败清理都不删除、不覆盖,切回播放列表仍保留同一 DOM 元素)。默认底部栏右侧为 ['volume', 'pip', 'menu', 'screenshot', 'live', 'fullscreen'],可经 ui.bottomBar.right 自定义。
设置菜单(MenuRenderer)提供倍速(0.5×–2×)、清晰度/ABR("自动" + 轨道列表)、音轨与字幕分区:当前倍速派生自 PlayerSnapshot.playbackRate;选中"自动"派发 quality 命令载荷 'auto'(启用 ABR),固定清晰度派发轨道下标(适配器关闭 ABR 后切换轨道);音轨分区由 supportsAudioTrack 能力位门控(WHEP/FLV 不出现;单音轨源不展示),选择派发 audiotrack 命令载荷分组键(language:role,语言偏好切换不关 ABR);字幕分区由 canSubtitle 能力位门控(WHEP/FLV 不出现;支持但无轨道源不展示死分区,单轨仍展示——用户需要开/关),首项为「关闭」,选择派发 subtitle 命令载荷轨道 id 或 'off',当前选中项 aria-checked 同步;trackschanged / adaptation / qualitychanged / audiotrackchanged / subtitlechanged / subtitletracks 后菜单自动刷新(内容签名覆盖轨道全部公开显示字段:同 id/active 但 label/language/kind 变化会重建字幕菜单项,列表完全不变不重建 DOM)。触发按钮具备 tooltip、aria-label、aria-haspopup 与 aria-expanded;Escape 关闭并还原焦点,点击播放器外部或切换源时关闭;菜单项为原生 button(键盘可聚焦)。
PlayerSnapshot.subtitleVersion 是字幕轨列表的独立刷新信号:每次 subtitletracks 与换源递增。因此只实现字幕能力的第三方适配器在默认关闭字幕(subtitle 仍为 undefined)时,也能立即触发 state-change 并更新菜单入口,不需要同时派发 trackschanged。
字幕显示(第一阶段起):采用 Shaka Player 5.2.4 / 浏览器原生文本渲染(selectTextTrack 选择即显示,cue 由浏览器自绘,内嵌与外挂轨均如此)——SubtitleRenderer 为无 DOM 的显式占位(保留渲染器结构与公开导出面),未来若需自绘 cue 排版再在此实装。外挂字幕(2026-09-22 第二阶段):PlayerConfig.subtitles(SubtitleSource[])经 Shaka 单 URL 源注入侧载 WebVTT,轨道/选择/事件/菜单全部走上述既有链路;单条加载失败派发 subtitleloadfailed(载荷脱敏 PlayerError,可选字幕不阻断主视频),详见 docs/api.md 外挂字幕节。
从带外挂字幕的 Shaka 源切到 WHEP/FLV 时,若本次请求未显式传入 subtitles,UI 不会把旧字幕当成本次新配置提交;切回 Shaka 可沿用已保存的字幕。显式把非空字幕提交给 WHEP/FLV 仍以 CONFIG_ERROR 拒绝,失败后 reload() 保持同一拒绝语义;显式传 subtitles: [] 可清除旧配置。
主题定制
PlayerUIOptions.theme 与 setTheme() 都接受 ThemeUpdate(Partial<ThemeTokens>)。未提供的令牌沿用当前值;getTheme() 返回副本,修改返回对象不会反向改变播放器。
文案字典(i18n)
PlayerUIOptions.strings 与 setStrings() 都接受 Partial<PlayerStrings>(语义槽位键,与 theme 同风格增量合并);getStrings() 返回完整副本。内置 DEFAULT_STRINGS(中文,迁移自各 Renderer 现值)与 STRINGS_EN(英文整包):
import { PlayerUI, STRINGS_EN } from '@mm-player/player-ui';
const player = new PlayerUI({ container: '#player', strings: STRINGS_EN });
player.setStrings({ fullscreen: 'Pantalla completa' }); // 运行时增量<mm-player> 经 property 设置(对象值不做属性反射):el.strings = { play: 'Play' }。唯一文案纠偏:宿主 aria-label 从 'Video Player' 统一为中文 '视频播放器'(可经 strings.playerRegion 覆盖)。
网络在线/离线联动
PlayerUI 监听 window offline/online(PlayerCore 保持 Headless 不感知 window/navigator)。navigator.onLine 只是设备网络提示,不代表媒体端点真实可达——本能力不据此伪造任何播放器状态,恢复结果由适配器真实事件回流。
- 离线提示:
offline时显示内置提示(懒创建、role="status"、pointer-events: none不阻挡控制栏交互),online时移除;构造选项offlineOverlay: false可关闭(默认开启,仅影响展示,不影响事件与恢复协调);文案经字典offline槽位(默认「网络连接已断开,恢复后将自动重连」,setStrings即时刷新);构造时已离线直接显示提示但不派发事件。 - 事件:
offlinechange(载荷boolean,true= 当前离线),仅真实状态变迁时派发(重复事件与快速抖动去重);事件由 PlayerUI 本地事件总线交付,尚未配置 URL、适配器加载中或适配器为空时同样可观察;当前值经isOffline()查询;<mm-player>桥接为同名 DOM CustomEvent。 - 恢复协调(离线不调用 play,只记录恢复意图——离线时刻快照 playing/loading):online 后按公开事实决策——适配器自动重连退避中不干预(退避定时器自然成功,不重置
retryCount/maxAttempts、不产生重复连接),恢复意图保持到reconnected、UI 恢复成功或用户操作,因此 online 后才到达的reconnectfailed/FLV·Shaka 网络错误仍可恢复;网络类终态错误执行单次reload()+play(),非网络错误不恢复。独立恢复代次使pause/stop/destroy/load/switchAdapter/reload/play可同步取消已经启动的旧恢复链;恢复中再次离线不调用 play,下一次 online 从已完成的 reload 阶段续接,不重复 reload。恢复失败被观察([PlayerUI] Network recovery failed:,无未处理拒绝)且不自动重试。destroy对称解绑全部监听并清空 UI 事件总线。
| 令牌 | 默认值 | 作用 |
| --- | --- | --- |
| --mm-theme-color | 97, 234, 253 | 主状态色 |
| --mm-accent-color | 97, 234, 253 | 进度、滑块和悬停强调色 |
| --mm-font-color | 255, 255, 255 | 控件前景色 |
| --mm-font-color-reverse | 0, 0, 0 | 反色区域 |
| --mm-bg-color | 0, 0, 0 | 视频区域背景色 |
| --mm-border-radius | 4px | 控件与进度条圆角 |
| --mm-control-height | 36px | 方形控制按钮尺寸 |
仓库完整示例的“主题定制”面板提供颜色选择器、圆角/控件高度滑块和实时 JSON,可直接作为可视化配置入口。
创建与使用插件
新插件应通过 type 显式声明接收的上下文,避免依赖旧版启发式识别:
import type { CorePlugin } from '@mm-player/player-core';
import type { UIPlugin, Plugin } from '@mm-player/player-ui';
let offState = () => {};
let watermarkNode: HTMLElement | null = null;
let onLegacyPlay = () => {};
const corePlugin: CorePlugin = {
type: 'core',
name: 'state-observer',
install({ stateManager }) {
offState = stateManager.subscribe((snapshot) => console.log(snapshot.status));
},
destroy() { offState(); }
};
const uiPlugin: UIPlugin = {
type: 'ui',
name: 'watermark',
install({ playerUI }) {
watermarkNode = document.createElement('span');
watermarkNode.textContent = 'LIVE';
playerUI.showOverlay(watermarkNode);
},
destroy() { watermarkNode?.remove(); }
};
const legacyPlugin: Plugin = {
type: 'legacy',
name: 'legacy-events',
install(playerUI) {
onLegacyPlay = () => console.log('play');
playerUI.on('play', onLegacyPlay);
},
uninstall(playerUI) { playerUI.off('play', onLegacyPlay); }
};
await player.addPlugin(corePlugin);
await player.addPlugin(uiPlugin);
await player.addPlugin(legacyPlugin);
await player.removePlugin('watermark');Core 插件接收 Headless 状态/命令上下文,UI 插件接收 DOM/UI 上下文,Legacy 插件直接接收 PlayerUI。removePlugin() 会执行对应清理并注销;插件必须自行对称释放订阅、DOM、计时器和外部资源。
Web Component <mm-player>
| 属性 / Attribute | 说明 |
| --- | --- |
| src / protocol / poster | 媒体源 / 协议 / 封面 |
| autoplay / loop / playsinline / muted / controls / volume | 布尔/数值属性,反射 |
方法:play / pause / stop / destroy / load / reload / switchAdapter / setVolume / setMuted / setTheme / setControlsVisible + 全屏/画中画/截图(enterFullscreen / exitFullscreen / toggleFullscreen / isFullscreen、canPip / enterPip / exitPip / togglePip / isPipActive、screenshot、returnToLive)+ 清晰度/ABR(getQualityTracks / getCurrentQuality / setAbrEnabled / isAbrEnabled / switchQuality)+ 音轨(getAudioTracks / getCurrentAudioTrack / setAudioTrack)+ 字幕(getSubtitleTracks / getCurrentSubtitleTrack / setSubtitleTrack)+ 播放列表切曲(next / prev / jumpTo,2026-09-27 起)+ 网络(isOffline)+ 文案字典 property(strings,不做属性反射)+ Overlay/Slot API(appendOverlay/removeOverlay/replaceOverlay/clearOverlay/appendPoster/removePoster/appendLeftControl/appendRightControl/removeControl)+ getCore/getPlayerUI/getShadowRoot。事件含 offlinechange(detail 为布尔载荷)。
快捷键(元素聚焦时):Space/Enter 播放暂停、←/→ 快退快进、↑/↓ 音量、F 全屏、M 静音——全部经 CommandBus 派发,与控制栏按钮共享同一处理器。
事件:ready / loaded / destroy / play / pause / ended / error / timeupdate / buffering / progress / statechange / themechange / overlaychange / controlchange / slotchange / resize / reconnecting / reconnected / reconnectfailed / offlinechange / unsupported + 清晰度/音轨/字幕/播放列表事件(qualitychanged / adaptation / trackschanged / audiotrackchanged / subtitlechanged / subtitletracks / subtitleloadfailed / playlistchanged,playlistchanged 的 detail 为 { index, total })+ 画中画/截图结果事件(pipchange / screenshotresult / screenshotfailed)。
生命周期与资源释放
PlayerUI构造时渲染 UI、创建VideoMediaTech、注入 Core、订阅StateManager。destroy顺序:取消 StateManager 订阅 → 销毁 Renderer → 卸载插件 → 清理 IconManager →await core.destroy()→ 清空 DOM → 释放引用。必须await。destroy幂等(destroyed标志守卫)。ended状态自动触发stop(由 state-change 驱动,仅转入 ended 时触发一次)。- Web Component 同一任务内移除再挂载视为 DOM 移动,不销毁 UI;若清理已经开始,重新连接会等待旧实例销毁后再初始化。
线程与回调约束
- UI 全部派生自
PlayerSnapshot(state-change 驱动),不直读 adapter/video/tech。 - 控件激活态由 state-change 驱动,不乐观置位(避免适配器早返回时 UI 假激活)。
- Renderer 互不引用,仅消费 Snapshot + 公开查询 API(
getQualityTracks/isAbrEnabled)+ dispatch。 - 主题/控件显隐运行时切换不重建 Renderer、不重新 render/mount。
- 全屏/画中画/截图等 DOM 命令由 PlayerUI 在 CommandBus 注册(Core 只注册媒体命令);
destroy时注销命令并解绑 PiP/全屏/截图监听,销毁后迟到的命令不再触碰已释放 UI。
常见错误与注意事项
container必填且必须存在;选择器找不到会抛错。volume范围 0–1(默认 1)。- 低带宽模式
pause实际执行stop(断开节省资源);高带宽模式仅暂停。 - ShadowRoot 宿主下样式经
adoptedStyleSheets隔离;HTMLElement 宿主用<style>。 destroy后不得再调用实例方法。- 构造时传入
url的初始加载与autoplay自动播放共用同一条 Promise 链:任一环节失败都会以[PlayerUI] Initial load or autoplay failed:记录到控制台并经 adaptererror事件进入错误态,不会产生 unhandledrejection;显式await player.play()的错误传播不受影响(自动播放仍受浏览器策略限制,通常需muted: true)。
UMD / <script> 标签使用
UMD 产物(dist/player-ui.umd.js,全局名 player-ui)支持直接 script 加载,但两个外部依赖必须先以各自的全局名就位:
<script src="node_modules/@mm-custom/method/dist/method.umd.js"></script> <!-- globalThis.method -->
<script src="node_modules/@mm-player/player-core/dist/player-core.umd.js"></script> <!-- window['player-core'] -->
<script src="node_modules/@mm-player/player-ui/dist/player-ui.umd.js"></script> <!-- window['player-ui'] -->
<script>
const { PlayerUI } = window['player-ui'];
</script>加载 player-ui.umd.js 时会立即注册 <mm-player> 自定义元素(模块作用域执行 customElements.define)。全局名在 vite 构建的 output.globals 中显式配置(player-core / method),与依赖包自身 UMD 构建的真实全局名一致;缺失任一依赖时,实例化(如 new PlayerUI(...))会在 new EventManager() 处抛 TypeError。
