news 2026/9/12 18:07:26

HyperFrames Player 完整指南:零依赖 Web Component 播放器架构、API 与集成实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HyperFrames Player 完整指南:零依赖 Web Component 播放器架构、API 与集成实践

HyperFrames Player 完整指南:零依赖 Web Component 播放器架构、API 与集成实践

【免费下载链接】hyperframesWrite HTML. Render video. Built for agents.项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes

@hyperframes/player是 HyperFrames 官方提供的可嵌入 Web Component 播放器,用于在任意网页或前端框架中播放 HyperFrames composition(由 HTML + GSAP 时间线构成的视频合成)。它零依赖、基于 Shadow DOM 与沙箱 iframe 运行,能自动探测 composition 的尺寸与时长、响应式缩放、处理移动端自动播放策略,并暴露一套完整的播放控制 API。读完本文,你将掌握该播放器的安装、属性配置、JavaScript API、运行时数据通道、沙箱隔离模型与底层工作原理,能够把它无缝接入 React、Vue 或纯 HTML 页面。

安装与加载方式

通过 npm 安装(当前仓库版本为 0.8.33,见 packages/player/package.json):

npm install @hyperframes/player

也可以直接用 CDN 以 ESM 模块方式加载:

<script type="module" src="https://cdn.jsdelivr.net/npm/@hyperframes/player"></script>

如果你的页面需要经典<script>标签而非 ESM,请使用显式的全局构建版本:

<script src="https://cdn.jsdelivr.net/npm/@hyperframes/player/dist/hyperframes-player.global.js"></script>

从 package.json 的exports字段可以看出,包还额外暴露了./slideshow子路径(对应dist/slideshow/hyperframes-slideshow.*),用于幻灯片播放器场景。三种分发格式(ESM / CJS / IIFE)均会生成压缩产物与 source map,并包含 TypeScript 类型声明。

基础用法

在最简单的场景下,你只需要一个自定义元素标签:

<hyperframes-player src="./my-composition/index.html" controls></hyperframes-player>

播放器会将 composition 加载进一个沙箱 iframe,自动探测其尺寸与时长,并按比例缩放以适配所在容器。

与前端框架集成

自定义元素注册后即可在任意框架中使用。以 TypeScript 为例:

import "@hyperframes/player"; // 自定义元素此时已注册,可直接在标记中使用 // React: <hyperframes-player src="..." controls /> // Vue: <hyperframes-player :src="url" controls />

由于底层是标准 Custom Element,React 与 Vue 都会把srccontrols等属性原样透传到 DOM,无需任何适配层。

Poster 封面图

在播放开始前显示一张静态封面图:

<hyperframes-player src="./composition/index.html" poster="./thumbnail.jpg" controls ></hyperframes-player>

调用play()时封面图会被自动移除。

属性(Attributes)详解

播放器通过以下属性控制行为,全部以 HTML 属性形式声明,并同步暴露为同名的 JS 属性(property):

属性类型默认值说明
srcstringcomposition HTML 文件的 URL
audio-srcstring音频 URL,用于父帧播放(移动端场景)
widthnumber1920composition 宽度(像素,用于宽高比计算)
heightnumber1080composition 高度(像素,用于宽高比计算)
controlsbooleanfalse显示播放/暂停、进度条与时间显示
mutedbooleanfalse静音
audio-lockedbooleanfalse强制静音并隐藏音量控件,观看者无法开启声音
posterstring播放开始前显示的图片 URL
playback-ratenumber1播放速度倍率(0.5 为半速,2 为两倍速)
autoplaybooleanfalse就绪后自动开始播放
loopbooleanfalsecomposition 结束时循环重播
shader-capture-scalenumber传递给浏览器预览的 shader 转场快照缩放(0.25~1
shader-loadingcomposition \| player \| nonecomposition控制 shader 转场预加载 UI 的归属

尺寸与宽高比

width/height定义的是 composition 的原生分辨率(用于宽高比计算),而不是播放器的显示尺寸。播放器实际显示大小由元素或父容器的 CSS 决定(见下文"Sizing"小节)。在 hyperframes-player.ts 中,这两个属性经由readPositiveDimension校验——NaN、零或负数都会被拒绝并回退到默认值 1920×1080,避免scale(NaN)或除零导致播放器空白(该保护逻辑定义在 composition-probe.ts)。

播放速率边界

playback-rate并非任意值都接受。源码中定义了MIN_PLAYBACK_RATE = 0.1MAX_PLAYBACK_RATE = 5(见 hyperframes-player.ts),超出范围的速率会被钳制,这既与 iframe 内 runtime 的钳制逻辑保持一致,也防止了极端值触发原生HTMLMediaElement.playbackRatesetter 抛错。

Shader 转场预览

当 composition 使用@hyperframes/shader-transitions时,播放器可以接管"仅预览用"的 shader 捕获设置:

<hyperframes-player src="./composition/index.html" shader-capture-scale="1" shader-loading="player" controls ></hyperframes-player>
  • shader-loading="player":由播放器根据 shader 进度消息显示"转场预加载"覆盖层;
  • shader-loading="composition"(默认):保留 composition 自身的回退行为;
  • shader-loading="none":完全抑制加载器。

从实现上看(shader-options.ts),这两个属性会被改写进 composition 的 URL 查询参数(__hf_shader_capture_scale__hf_shader_loading)或 srcdoc 的<head>脚本(window.__HF_SHADER_CAPTURE_SCALEwindow.__HF_SHADER_LOADING)中。注意一个工程细节:改写 URL 时只删除/追加播放器自己的两个 key,其余查询参数按字节原样透传,避免URLSearchParams表单编码(空格变+)破坏 composition 作者原本的查询串。shader-capture-scale会被钳制在0.25~1之间。

音频锁(宿主强制的静音播放)

audio-locked会强制开启muted并隐藏音量控件,观看者没有任何 UI 路径可以重新打开声音。它适用于嵌入聊天宿主(如 Claude.ai、ChatGPT 等)的场景——无论观看者意图如何,音频都必须保持关闭。直接设置muted是不够的,观看者仍可通过控制栏把它翻转回来。

移除audio-locked只会重新显示控件,不会自动取消静音;调用方需要在解锁后显式管理muted

宿主环境回退机制。某些宿主渲染器(尤其是 Claude 桌面版 Electron 客户端)会在属性到达 DOM 前剥离未知的自定义元素属性,导致该属性失效。作为安全网,播放器还会在检测到此类环境时自行施加锁定——通过navigator.userAgent匹配Claude/\d+Electron特征(见 hyperframes-player.ts),即使属性从未到达,音频也保持静音。公开的audioLocked属性仍然只反映属性本身,因此外部消费者(例如镜像状态的宿主 widget)不会受回退逻辑影响。

移动端音频

移动端浏览器会阻止 iframe 内部的audio.play()——当用户手势发生在父帧时,User Activation 规范不会通过postMessage把激活状态传播到 iframe 边界。播放器对同源 iframe(默认情况,sandbox包含allow-same-origin)自动处理此问题:

  1. composition 就绪后,播放器从 iframe DOM 中提取所有带时间轴的媒体元素(audio[data-start]video[data-start]),并在父帧创建副本;
  2. iframe 内的原件被禁用(移除srcdata-start),runtime 不会尝试播放它们;
  3. play()从用户手势调用时,父帧媒体的.play()在手势调用栈中同步执行,满足移动端自动播放策略;
  4. 父帧媒体与 GSAP 时间线同时启动并自由运行——两者都是实时系统,无需主动同步。

这一机制由ParentMediaManager实现(parent-media.ts):它会为每个代理条目维护镜像的currentTime,仅当连续 2 个采样点漂移超过 50ms(MIRROR_DRIFT_THRESHOLD_SECONDS = 0.05MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES = 2)才回写校正,从而吸收单次采样抖动而不频繁跳变。它还通过MutationObserver监听 iframe 内动态新增/移除的媒体元素,支持子 composition 激活等运行时变化。消费者无需任何改动——开箱即用。

可选的audio-src属性可以在 iframe 加载前就开始预加载主音轨(慢网速场景有用),但移动端播放并不依赖它。

JavaScript API

const player = document.querySelector("hyperframes-player"); // 播放控制 player.play(); player.pause(); player.seek(2.5); // 跳转到 2.5 秒 // 属性 player.currentTime; // number(可读写) player.duration; // number(只读) player.paused; // boolean(只读) player.ready; // boolean(只读) player.playbackRate; // number(可读写) player.muted; // boolean(可读写) player.audioLocked; // boolean(可读写)——强制静音并隐藏音量控件 player.loop; // boolean(可读写) player.shaderCaptureScale; // number(可读写) player.shaderLoading; // "composition" | "player" | "none"(可读写) // 内部 iframe 访问(进阶用户使用,见"进阶:iframe 访问") player.iframeElement; // HTMLIFrameElement(只读)

seek()的实现同时携带秒与帧两个维度:timeSeconds供协议 v1 的 runtime 使用,frametimeSeconds × runtimeFps)兼容旧版 runtime,保证跨源嵌入可定位播放。此外还有stopMedia()(停止 iframe 与父帧媒体)、volumescenes(最近一次 runtime timeline 消息中的场景列表)等成员。在支持直接访问 GSAP 时间线的场景中(window.__timelines且无 runtime),seek会直接驱动时间线并在暂停时触发onUpdate,使依赖根时间线onUpdate驱动场景显隐的 composition(如幻灯片 deck)在暂停态也能重绘。

运行时数据通道(Runtime Data Delivery)

setRuntimeData(channel, payload)会先克隆并保留 payload,然后在 composition runtime 就绪后投递。非法 channel 与不可克隆的 payload 会同步抛错;调用返回之后的失败通过runtimedataerror事件上报,成功应用则触发runtimedataapplied。两个事件都携带{ channel, requestId },错误还附带message。当投递成败很关键时,应同时监听两个结果:

player.addEventListener("runtimedataapplied", ({ detail }) => { console.log("applied", detail.channel, detail.requestId); }); player.addEventListener("runtimedataerror", ({ detail }) => { console.error("not applied", detail.channel, detail.requestId, detail.message); }); player.setRuntimeData("captions", captionData);

实现细节(hyperframes-player.ts):

  • channel 必须匹配/^[a-z][a-z0-9-]{0,63}$/,否则同步抛出Invalid HyperFrames runtime-data channel
  • payload 通过structuredClone深拷贝后保留,若环境不支持structuredClone则直接拒绝(防止未验证的 payload 进入运行时);
  • 每个 channel 的投递设有 10 秒超时(RUNTIME_DATA_DELIVERY_TIMEOUT_MS = 10_000),超时、iframe 被销毁或桥接失败都会发出runtimedataerror,而不是无限挂起;同一 channel 只有最新的一次在途投递可以产生完成事件;
  • 投递优先走 iframe 内window.__hyperframes暴露的直接桥接,其次回退到postMessage控制消息;当 runtime 在播放器已发出消息之后才注册监听时(如热缓存重载、Claude 桌面客户端),runtime 的ready信号会触发桥接状态重放,保证消息不丢失。

clearRuntimeData(channel)用于清除某个 channel 的保留数据并同步通知 runtime。

进阶:iframe 访问与沙箱模型

composition 运行在播放器 Shadow DOM 内一个沙箱化<iframe>中。默认沙箱包含allow-same-origin——这是为编辑器、录制器与自定义时间线集成准备的"可信内容"模式,而非隔离边界:同源 composition 代码可以触达嵌入页面。

对于只读或消息桥接型集成,请设置sandbox-origin="opaque"。任何非空值都会被当作 opaque 处理(拼写错误不会削弱隔离)。该属性变更会重新加载当前 composition,因为浏览器沙箱变更只在导航时生效。Opaque 模式会移除allow-same-origin但保留脚本能力,阻止 composition 读取无关的父 DOM;此时contentDocument__player__timelines的直接访问均不可用。

构建需要直接访问的可信编辑器集成时,使用iframeElementgetter:

const player = document.querySelector("hyperframes-player"); const iframe = player.iframeElement; // 现在可以深入 composition 的 DOM 与运行时 iframe.contentDocument.querySelectorAll("[data-composition-id]"); iframe.contentWindow.__timelines;

这是把播放器桥接到@hyperframes/studio等工具的标准方式。studio 导出的resolveIframehelper 同时兼容 iframe ref 与 web-component ref:

import { useTimelinePlayer, resolveIframe } from "@hyperframes/studio"; const { iframeRef } = useTimelinePlayer(); const player = document.createElement("hyperframes-player"); player.setAttribute("src", src); container.appendChild(player); // 把内部 iframe 转发给 useTimelinePlayer 以驱动播放/暂停/seek。 iframeRef.current = resolveIframe(player);

React:声明式 ref 模式

如果你偏好 JSX 而非命令式创建元素,可以把 ref 直接挂到 web component 上,再在 effect 中解析 iframe:

import "@hyperframes/player"; import type { HyperframesPlayer } from "@hyperframes/player"; import { useTimelinePlayer, resolveIframe } from "@hyperframes/studio"; function StudioPreview({ src }: { src: string }) { const { iframeRef, onIframeLoad } = useTimelinePlayer(); const playerRef = useRef<HyperframesPlayer>(null); useEffect(() => { iframeRef.current = resolveIframe(playerRef.current); }); return <hyperframes-player ref={playerRef} src={src} onLoad={onIframeLoad} />; }

常见陷阱提醒

如果你把<hyperframes-player>元素本身(而非iframeElement)传给一个期望<iframe>的 hook,每次.contentWindow/.contentDocument访问都会返回null,因为 iframe 位于播放器的 Shadow DOM 内部。务必先取出iframeElement,或使用能透明处理 iframe 与 web-component 两种宿主的resolveIframe

事件(Events)

事件Detail触发时机
ready{ duration }composition 加载完成且时长已确定
play播放开始
pause播放暂停
timeupdate{ currentTime }播放位置变化(约 10 fps)
ended到达结尾(非循环模式)
error{ message }composition 加载失败
shadertransitionstate{ compositionId, state }shader 转场缓存/捕获进度
player.addEventListener("ready", (e) => { console.log(`Duration: ${e.detail.duration}s`); }); player.addEventListener("ended", () => { console.log("Done!"); });

除上述事件外,源码中还可能派发ratechangevolumechangescenesaudioownershipchangeplaybackerrorruntimeprotocolerror等事件(分别对应播放速率/音量变化、场景列表更新、音频所有权转移、代理播放失败与运行时协议版本不兼容),其中runtimeprotocolerror携带{ code, receivedVersion },用于检测 composition 内 runtime 协议版本与播放器不兼容的情况。

尺寸控制(Sizing)

播放器填满其容器,并在保持宽高比的前提下把 composition 缩放到适配。请在元素或其父容器上设置尺寸:

hyperframes-player { width: 100%; max-width: 800px; aspect-ratio: 16 / 9; }

widthheight属性定义的是 composition 原生分辨率,仅用于宽高比计算,不决定播放器显示尺寸。底层的缩放实现在 iframe-dom.ts:iframe 保持 composition 原生像素尺寸,通过transform: translate(-50%, -50%) scale(min(w/W, h/H))等比缩放;若播放器尚未获得绘制尺寸(0×0),缩放会安全地空操作,并在 ready 后仍无法缩放时输出一次带诊断信息的console.warn(避免隐藏/零尺寸播放器刷屏)。

工作原理(How It Works)

播放器在 Shadow DOM 内的沙箱<iframe>中渲染 composition,通过postMessage与 HyperFrames runtime 通信。iframe 初始沙箱为allow-scripts; allow-same-originallow="autoplay; fullscreen"referrerPolicy="no-referrer"(见 iframe-dom.ts)。

启动探测(Composition Probe)

iframe 加载完成后,CompositionProbe(composition-probe.ts)以 200ms 间隔轮询最多 40 次(约 8 秒),依次识别:

  • runtime 桥接(window.__hfwindow.__player)——若存在且getDuration()返回正时长,立即就绪;
  • GSAP 时间线(window.__timelines)——解析为"直接时间线适配器",由播放器直接驱动,无需 runtime;
  • 嵌套 composition(DOM 中存在[data-composition-src]子元素)——此时必须立即注入 runtime,因为嵌套子场景本身依赖 runtime 懒加载。

Runtime 自动注入

如果 composition 只有 GSAP 时间线而没有 runtime,播放器会从 CDN 自动注入 runtime 脚本。注入时机由 shouldInjectRuntime.ts 决定:

  • 嵌套 composition:立即注入。若等待,composition 内常见的"预览期内联gsap.timeline"会以不完整时长注册到__timelines["main"],导致播放器被锁定在一个残缺时间线上;
  • 自包含 composition:先给直接时间线适配器 5 个轮询 tick 的宽限,若适配器未出现再注入 runtime 作为回退。

runtime CDN URL 由 runtime-url.ts 统一管理(https://cdn.jsdelivr.net/npm/@hyperframes/core@<version>/dist/hyperframe.runtime.iife.js),保证探测期的晚期注入与 srcdoc 解析期注入不会漂移到不同 URL。

postMessage 协议

iframe 内的 runtime 以source: "hf-preview"向父帧发送消息,消息处理器(runtime-message-handler.ts)只接受来自自身 iframe 的消息,并校验运行时协议版本。主要消息类型包括:

  • state{ frame, isPlaying },更新播放位置与暂停状态;
  • timeline:携带durationInFrames/durationSeconds/compositionWidth/compositionHeight/scenes,是跨源 readiness 信号(同源探测无法检查 CDN iframe);
  • stage-size:更新 composition 尺寸(带有限性检查,防止 Infinity 把 iframe 缩到 0);
  • ready:触发桥接状态重放(静音、音量、播放速率等);
  • media-autoplay-blocked:触发父帧媒体代理接管(promoteToParentProxy);
  • shader-transition-state:更新 shader 转场加载进度;
  • runtime-data-applied/runtime-data-error:运行时数据投递的结果确认。

反过来,播放器向 iframe 发送source: "hf-parent"的控制消息(playpauseseekset-mutedset-volumeset-playback-ratetick等)。在父帧可能被 Chromium 节流 iframe 自身 rAF 的场景(如 Electron / Claude 桌面深层嵌套跨源 iframe),播放器会启动一个父帧 RAF 时钟持续发送tick,确保动画持续推进。

分发格式(Distribution)

格式文件适用场景
ESMhyperframes-player.js打包器(Vite、webpack 等)
CJShyperframes-player.cjsNode.js /require()
IIFEhyperframes-player.global.js<script>标签、CDN

所有格式均压缩并附带 source map,包含 TypeScript 类型定义。

许可证

MIT。

如果你需要更进一步,可以继续阅读仓库内的 player 源码、浏览器沙箱测试 与 性能测试场景,或查看 studio 集成文档 了解如何把播放器桥接进完整的时间线编辑工作流。

【免费下载链接】hyperframesWrite HTML. Render video. Built for agents.项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 18:06:21

大模型调参实战:Temperature与Top-P原理与应用

1. 大模型调参的双刃剑&#xff1a;Temperature与Top-P的本质解析 作为在AI领域摸爬滚打多年的老手&#xff0c;我见过太多开发者对着大模型的输出结果挠头——为什么同样的提示词&#xff0c;有时能产生逻辑严谨的代码&#xff0c;有时却冒出天马行空的诗句&#xff1f;这背后…

作者头像 李华
网站建设 2026/9/12 18:04:53

强化学习数学原理:MDP与贝尔曼方程解析

1. 项目概述《强化学习的数学原理》是赵世钰教授关于强化学习理论基础的经典著作&#xff0c;第九章作为全书的重要章节&#xff0c;深入探讨了强化学习中的核心数学概念和算法原理。作为一位长期从事机器学习研究的工程师&#xff0c;我发现这一章的内容对于理解强化学习的底层…

作者头像 李华
网站建设 2026/9/12 18:01:40

Spring Boot与MQTT构建高效物联网监控系统

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 18:01:11

Lucide for Vue 集成指南:从安装到进阶定制的完整实践

Lucide for Vue 集成指南&#xff1a;从安装到进阶定制的完整实践 【免费下载链接】lucide Beautiful & consistent icon toolkit made by the community. Open-source project and a fork of Feather Icons. 项目地址: https://gitcode.com/GitHub_Trending/lu/lucide …

作者头像 李华