mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-03 04:07:03 +00:00
* docs(studio): the browser editor in six pages, named the browser editor * docs(studio): mute where the editor has it, and no solo control The audio pages still described a solo control and a group-row mute that the editor does not render. Muting is the speaker button on an audio track (Mute track / Unmute track), the same toggle on a selected clip, and the Muted switch; a group is silenced through its members' tracks. The audio reference drops its solo section for the same reason.
161 lines
5.8 KiB
Plaintext
161 lines
5.8 KiB
Plaintext
---
|
|
title: "Animate with GSAP"
|
|
sidebarTitle: "GSAP animation"
|
|
description: "Create a paused timeline that HyperFrames can seek to any frame."
|
|
---
|
|
|
|
import { DocsVideo } from "/snippets/docs-video.jsx";
|
|
|
|
GSAP is the primary animation runtime for HyperFrames compositions. You author the motion; HyperFrames owns the playhead.
|
|
|
|
|
|
<DocsVideo
|
|
title="GSAP keyframes on screen, and the ease curve being shaped in the browser editor"
|
|
src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/gsap-keyframes-demo-v2.mp4"
|
|
poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/gsap-keyframes-demo-v1.jpg"
|
|
/>
|
|
|
|
The code in that clip is the real thing — a `gsap.to()` with a `keyframes` array,
|
|
and the same motion being reshaped by dragging the ease curve in the browser editor rather
|
|
than guessing numbers.
|
|
|
|
## Minimal contract
|
|
|
|
Give the composition a finite duration, create a paused timeline, and register it under the composition ID:
|
|
|
|
```html
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>
|
|
</head>
|
|
<body>
|
|
<div
|
|
id="root"
|
|
data-composition-id="intro"
|
|
data-start="0"
|
|
data-duration="3"
|
|
data-width="1920"
|
|
data-height="1080"
|
|
>
|
|
<h1 id="title" class="clip" data-start="0" data-duration="3" data-track-index="0">
|
|
HyperFrames
|
|
</h1>
|
|
</div>
|
|
|
|
<script>
|
|
const timeline = gsap.timeline({ paused: true });
|
|
|
|
timeline.fromTo(
|
|
"#title",
|
|
{ opacity: 0, y: 32 },
|
|
{ opacity: 1, y: 0, duration: 0.6, ease: "power3.out" },
|
|
0,
|
|
);
|
|
|
|
window.__timelines = window.__timelines || {};
|
|
window.__timelines.intro = timeline;
|
|
</script>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
The registry key must match `data-composition-id`.
|
|
|
|
## Rules that matter
|
|
|
|
1. Create the timeline with `{ paused: true }`.
|
|
2. Register it on `window.__timelines`.
|
|
3. Give important tweens an explicit position.
|
|
4. Prefer `fromTo()` when both endpoints matter; it remains reliable after backward or random seeks.
|
|
5. Animate transforms and opacity for movement. Avoid repeatedly animating layout properties such as `top`, `left`, `width`, or `height`.
|
|
6. Let HyperFrames control clip visibility and media playback.
|
|
|
|
The normal timeline methods are `to()`, `from()`, `fromTo()`, and `set()`. GSAP supports many CSS properties, but the [HyperFrames animation skill](https://github.com/heygen-com/hyperframes/tree/main/skills/hyperframes/animation) contains the render-safe patterns used by agents.
|
|
|
|
## Load fonts before measuring text
|
|
|
|
An underline or a position calculated from text width needs the final font metrics. HyperFrames waits for fonts used by existing markup before starting composition scripts, but a face used only by script-created text may still be unloaded.
|
|
|
|
Request that face with `document.fonts.load()` before measuring. Match the text's font family, weight, and size, and pass the text so the browser can load the required character ranges. `document.fonts.ready` waits for fonts already requested; it does not request an unused face.
|
|
|
|
This example uses the `intro` composition, an inline-block `#title` styled with `font: 700 64px "LocalFace"` from a local `@font-face`, and a block-level `#underline`:
|
|
|
|
```js
|
|
const title = document.getElementById("title");
|
|
const underline = document.getElementById("underline");
|
|
title.textContent = "Build with HyperFrames";
|
|
|
|
document.fonts.load('700 64px "LocalFace"', title.textContent).then(() => {
|
|
underline.style.width = `${title.getBoundingClientRect().width}px`;
|
|
|
|
const timeline = gsap.timeline({ paused: true });
|
|
timeline.fromTo(
|
|
["#title", "#underline"],
|
|
{ opacity: 0, y: 32 },
|
|
{ opacity: 1, y: 0, duration: 0.6, ease: "power3.out" },
|
|
0,
|
|
);
|
|
|
|
window.__timelines = window.__timelines || {};
|
|
window.__timelines.intro = timeline;
|
|
});
|
|
```
|
|
|
|
The underline now matches the loaded text width. Build all tweens before registering the timeline at the end of the callback. Registering an empty timeline before the asynchronous build finishes can make a nested composition appear ready before its animation exists.
|
|
|
|
## Duration
|
|
|
|
An explicit root `data-duration` is the composition's render length:
|
|
|
|
```html
|
|
<div data-composition-id="demo" data-duration="12"></div>
|
|
```
|
|
|
|
This is the clearest choice for a complete composition. If the root omits `data-duration`, HyperFrames can infer length from registered timelines and supported media after the page initializes.
|
|
|
|
Do not assume a long source video automatically extends a shorter explicit root duration. The authored duration is the output window.
|
|
|
|
## Media belongs to the runtime
|
|
|
|
Do not play or seek media from the GSAP timeline:
|
|
|
|
```js
|
|
// Do not do this.
|
|
document.querySelector("video").play();
|
|
document.querySelector("audio").currentTime = 5;
|
|
```
|
|
|
|
Use media timing attributes instead. If a video needs to move or resize, animate a wrapper around it rather than the video element itself.
|
|
|
|
## Nested compositions
|
|
|
|
Each nested composition owns and registers its own timeline. The parent decides when the nested scene appears through its host element:
|
|
|
|
```html
|
|
<div
|
|
data-composition-id="intro"
|
|
data-composition-src="compositions/intro.html"
|
|
data-start="2"
|
|
data-duration="4"
|
|
></div>
|
|
```
|
|
|
|
Do not manually add the child timeline to the parent's GSAP timeline. HyperFrames maps the parent playhead into the nested scene.
|
|
|
|
## Verify the motion
|
|
|
|
```bash
|
|
npx hyperframes lint
|
|
npx hyperframes check
|
|
npx hyperframes snapshot --at 0,0.6,2.9
|
|
```
|
|
|
|
Check the first frame, the moving state, and the end state. A timeline that looks correct only during continuous browser playback may still fail when the renderer seeks directly to a frame.
|
|
|
|
## Related topics
|
|
|
|
- [Edit animation and keyframes in the browser editor](/studio/canvas#animate-with-keyframes)
|
|
- [Understand deterministic rendering](/concepts/determinism)
|
|
- [Use the HTML composition schema](/reference/html-schema)
|