@hyperframes /播放器
用于播放超帧组合的可嵌入web组件。零依赖,适用于任何框架。
安装
npm install @hyperframes/player
或者直接通过CDN加载:
<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>
使用
<hyperframes-player src="./my-composition/index.html" controls></hyperframes-player>
玩家在沙盒iframe中加载组件,自动检测其尺寸和持续时间,并根据容器进行相应缩放。
添加或删除 controls 随时都可以。当显示时,控件反映当前播放状态、位置和速度。
有一个框架
import "@hyperframes/player";
// The custom element is now registered — use it in your markup
// React: <hyperframes-player src="..." controls />
// Vue: <hyperframes-player :src="url" controls />
视频文件
集 type 到一个视频类型,播放器播放 src 在一个 <video playsinline> 而不是加载它作为一个组合。相同的API和事件适用: play(), pause(), seek(), currentTime, duration, ready, timeupdate, play, pause, ended, durationchange 和 resize,视频本身的大小为 compositionWidth/compositionHeight。一个无法加载的视频 error 立刻与 { message, code },在那里 code 是 MediaError 代码。一个 play() 浏览器会阻止(例如,未静音的自动播放)触发 playbackerror 与 { source: "video" } 让玩家暂停。 poster, controls, loop, muted, volume, playback-rate 和 autoplay 就像他们写作文一样。 scenes, setRuntimeData(), setColorGrading(), iframeElement 和 stopMedia() 是否仅限合成:视频没有场景或运行时,其iframe保持空白,以及 stopMedia() 不能停止视频。 srcdoc 赢得视频 src,就像在iframe中一样。
<hyperframes-player
type="video/mp4"
src="./render.mp4"
poster="./poster.jpg"
controls
></hyperframes-player>
海报图片
在播放开始前显示静态图像:
<hyperframes-player
src="./composition/index.html"
poster="./thumbnail.jpg"
controls
></hyperframes-player>
属性
| 属性 | 类型 | 默认的 | 描述 |
|---|---|---|---|
src | 字符串 | - - - - - - | URL指向合成HTML文件,或指向视频文件 type |
type | 字符串 | - - - - - - | 一、 video/... 类型(如。 video/mp4)玩 src 作为视频文件 |
audio-src | 字符串 | - - - - - - | 用于父框架播放的音频URL(移动) |
width | 数量 | 1920 | 合成宽度(以像素为单位) |
height | 数量 | 1080 | 合成高度(以像素为单位) |
controls | 布尔 | 假 | 显示播放/暂停,洗涤器和时间显示 |
muted | 布尔 | 假 | 静音音频回放 |
audio-locked | 布尔 | 假 | 强制静音和隐藏音量控制,使观众无法打开声音 |
poster | 字符串 | - - - - - - | 播放开始前显示的图像URL |
playback-rate | 数量 | 1 | 速度倍增器(0.5 =一半,2 =两倍) |
autoplay | 布尔 | 假 | 准备好了就开始玩 |
loop | 布尔 | 假 | 当合成结束时重新开始 |
shader-capture-scale | 数量 | - - - - - - | 着色器过渡快照比例转发到浏览器预览(0.25-1) |
shader-loading | composition | player | none | composition | 控制着色过渡准备加载UI所有权 |
assets-loading-ui | player | none | player | none 从不显示载入资产卡;资产事件仍在触发 |
low-power-idle | 布尔 | 假 | 暂停时,每秒检查一次,而不是每80毫秒检查一次(多玩家页面) |
disable-click-to-play | 布尔 | 假 | 点击播放器不再播放或暂停(主机覆盖自己的点击) |
no-load-timeout | 布尔 | 假 | 慢速负载在8秒时从不失败;页面拥有截止日期。错误仍然失败 |
range-start | 数量 | - - - - - - | 电影秒播放开始,循环回和公园时暂停 |
range-end | 数量 | - - - - - - | 胶片第二帧范围结束之前:它停止或循环在它之前的帧 |
range-start 和 range-end 播放电影的片段[开始,结束]。玩家停在 range-start 在 ready当一个暂停的弹头落在新的范围之外时; play() 从外面的范围开始。最后保存之前的最后一帧 range-end 和火灾 ended,或包裹到 range-start 与 loop。当前运行时停止于该帧本身;视频和 __timelines 玩家会在下一个时钟滴答声中停下来,然后返回;当玩家看到时间经过终点时,旧的运行时就会停止。 currentTime 和 duration 保持电影时间。超过胶片的范围被剪到尽头,空的或负的范围被忽略;两个火 rangeclamped.
着色过渡预览
当一个合成使用 @hyperframes/shader-transitions,玩家可以拥有仅预览的着色器捕获设置:
<hyperframes-player
src="./composition/index.html"
shader-capture-scale="1"
shader-loading="player"
controls
></hyperframes-player>
shader-loading="player" 显示来自着色器进度信息的玩家拥有的过渡准备叠加。 composition 只留下直接组合回退行为,和 none 抑制加载器。
Loading-assets卡
而图像、视频或字体仍在加载 ready,玩家在帧上显示一张加载卡片,并设置 assets-loading 属性。绘制自己的加载状态的主机可以使用 assets-loading-ui="none" (或 player.assetsLoadingUi = "none"). 这个 assets-loading 属性和 assetsready 和 painted 无论哪种方式,事件的行为都是相同的,因此主机仍然知道帧何时准备好。
音频锁定(主机强制静音播放)
audio-locked 部队 muted 打开和隐藏音量控制,没有UI路径为观众打开声音。使用它时,嵌入在聊天主机(克劳德。ai, ChatGPT等),无论观众的意图如何,音频都必须关闭。设置 muted 直接是 不 足够了——观看者可以通过控制条将其翻转回来。
删除 audio-locked 只隐藏控件;它 不 auto-unmute。电话管理 muted 在解锁后显式地。
宿主环境回退。 一些主机渲染器——尤其是Claude桌面电子客户端——在到达DOM之前剥离未知的自定义元素属性,从而破坏了属性。作为安全网,当玩家检测到这样的环境时,他们也会自己施加锁 navigator.userAgent,因此即使该属性从未到达,音频也保持静音。公众 audioLocked 属性仍然只反映属性,因此外部消费者(例如镜像状态的宿主小部件)不受回退的影响。
移动音频
移动浏览器阻塞 audio.play() 当用户手势发生在父框架中时,在iframes内部 用户激活规范 不传播激活跨帧边界通过 postMessage)。
播放器自动处理同源iframe(默认- sandbox 包括 allow-same-origin):
- 当合成准备好时,玩家提取所有定时媒体(
audio[data-start],video[data-start]),并创建父框架副本。 - iframe原件被禁用(
src和data-start删除),所以运行时不会尝试播放它们。 - 当
play()调用(从用户手势),父媒体.play()在手势调用栈中同步运行,满足移动自动播放策略。 - 母媒体和GSAP时间轴同时启动并且自由运行——不需要主动同步,因为它们都是实时系统。
消费者不需要进行任何更改——这是开箱即用的。
可选 audio-src 属性可用于在iframe加载之前开始预加载主音轨(在慢速连接上很有用),但不需要用于移动播放。
JavaScript API
const player = document.querySelector("hyperframes-player");
// Playback
player.play();
player.pause();
player.seek(2.5); // jump to 2.5 seconds
// Properties
player.currentTime; // number (read/write)
player.duration; // number (read-only)
player.paused; // boolean (read-only)
player.ready; // boolean (read-only)
player.compositionWidth; // number (read-only), the composition's width
player.compositionHeight; // number (read-only), the composition's height
player.disableClickToPlay; // boolean (read/write)
player.playbackRate; // number (read/write)
player.muted; // boolean (read/write)
player.audioLocked; // boolean (read/write) — force-mute + hide volume controls
player.loop; // boolean (read/write)
player.rangeStart; // number | null (read/write, mirrors range-start)
player.rangeEnd; // number | null (read/write, mirrors range-end)
player.shaderCaptureScale; // number (read/write)
player.shaderLoading; // "composition" | "player" | "none" (read/write)
// Inner iframe access (for advanced consumers — see "Advanced: iframe access" below)
player.iframeElement; // HTMLIFrameElement (read-only)
运行时数据传递
setRuntimeData(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);
只有通道的最新动态更新才能触发完成。缺少运行时响应;
如果框架拆除,或发出桥交付失败 runtimedataerror 而不是等待
下去。
Advanced: iframe访问
合成在沙盒中运行 <iframe> 在玩家的Shadow DOM中。默认的沙箱包括 allow-same-origin 用于检查组合DOM的编辑器、记录器和自定义时间轴集成。这是一种可信内容模式,而不是隔离边界:同源组合代码可以到达嵌入页面。
对于只读或消息桥接集成,请设置 sandbox-origin="opaque"。任何非空值都是
被视为不透明的,因此错别字不会削弱隔离性。更改属性会重新加载活动
因为浏览器沙盒更改只对导航生效。不透明模式
allow-same-origin 同时保留脚本,并防止与阅读无关的作文
父DOM。直接 contentDocument, __player, __timelines 访问是有意的
该模式不可用。
如果您正在构建需要直接访问的可信编辑器集成,请使用 iframeElement 吸气剂:
const player = document.querySelector("hyperframes-player");
const iframe = player.iframeElement;
// Now you can reach into the composition's DOM and runtime
iframe.contentDocument.querySelectorAll("[data-composition-id]");
iframe.contentWindow.__timelines;
这是将玩家与工具连接起来的典型方法 @hyperframes/studio。该工作室出口了 resolveIframe 与iframe refs和web组件refs一起工作的助手:
import { useTimelinePlayer, resolveIframe } from "@hyperframes/studio";
const { iframeRef } = useTimelinePlayer();
const player = document.createElement("hyperframes-player");
player.setAttribute("src", src);
container.appendChild(player);
// Forward the inner iframe so useTimelinePlayer can drive play/pause/seek.
iframeRef.current = resolveIframe(player);
React:声明式引用模式
如果你更喜欢JSX而不是命令式元素创建,直接在web组件上附加一个ref,并在效果中解析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>,每一个.contentWindow/.contentDocument访问的回报null因为iframe存在于玩家的Shadow DOM中。总是提取iframeElement首先,还是使用resolveIframe从@hyperframes/studio它透明地处理iframe和web组件主机。
事件
| 事件 | 细节 | 时触发 |
|---|---|---|
ready | { duration, compositionWidth, compositionHeight } | 加载成分并确定持续时间 |
durationchange | { duration } | 持续时间在 ready |
resize | { compositionWidth, compositionHeight } | 构图的大小改变了 |
play | - - - - - - | 开始播放 |
pause | - - - - - - | 播放暂停 |
timeupdate | { currentTime } | 播放位置改变(~10 fps) |
ended | - - - - - - | 到达终点(没有循环时) |
rangeclamped | { rangeStart, rangeEnd, duration } | 这个范围被剪掉了,或者被忽略了 |
error | { message } (视频模式: { message, code }) | 未能加载合成或视频 |
playbackerror | { source, error } | 浏览器阻止播放 |
shadertransitionstate | { compositionId, state } | 着色器过渡缓存/捕获进度 |
player.addEventListener("ready", (e) => {
console.log(`Duration: ${e.detail.duration}s`);
});
player.addEventListener("ended", () => {
console.log("Done!");
});
分级
玩家填充它的容器并在保持长宽比的同时缩放构图。设置元素或其父元素的大小:
hyperframes-player {
width: 100%;
max-width: 800px;
aspect-ratio: 16 / 9;
}
这个 width 和 height 属性定义了用于宽高比计算的合成的原生分辨率——它们不设置播放器的显示尺寸。
它是如何工作的
玩家在沙盒中渲染构图 <iframe> 在一个Shadow DOM里面。与HyperFrames运行时通信 postMessage。如果作文有GSAP时间表(window.__timelines),但没有运行时,播放器自动注入它从CDN。
分布
| 格式 | 文件 | 用例 |
|---|---|---|
| ESM | hyperframes-player.js | 捆绑器(Vite, webpack等) |
| cj | hyperframes-player.cjs | Node.js / require() |
| IIFE | hyperframes-player.global.js | <script> 标签,CDN |
所有格式都使用源映射进行缩小。包括TypeScript定义。
许可证
麻省理工学院