Files
heygen-com--hyperframes/skills/hyperframes-animation/examples/workflow-approve-press.html
T
wehub-resource-sync 85453da49f
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
Docs / Validate docs (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Sync skills to ClawHub / Publish changed skills (push) Waiting to run
regression / regression-shards (style-16-prod style-9-prod style-17-prod iframe-render-compat variables-prod mp4-h265-sdr, shard-4) (push) Has been cancelled
regression / regression-shards (style-4-prod style-11-prod style-2-prod animejs-adapter typegpu-adapter parallel-capture-regression, shard-5) (push) Has been cancelled
regression / regression-shards (style-7-prod style-8-prod style-10-prod css-spinner-render-compat webm-transparency mp4-h264-sdr webm-vp9, shard-3) (push) Has been cancelled
regression / regression-shards (sub-composition-video style-18-prod raf-ball-render-compat font-variant-numeric sub-comp-t0 sub-comp-id-selector, shard-7) (push) Has been cancelled
Windows render verification / Detect changes (push) Has been cancelled
Windows render verification / Preflight (lint + format) (push) Has been cancelled
Windows render verification / Render on windows-latest (push) Has been cancelled
Windows render verification / Tests on windows-latest (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Fallow audit (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Producer: integration tests (push) Has been cancelled
CI / Producer: unit tests (push) Has been cancelled
CI / File size check (push) Has been cancelled
CI / Test: skills (push) Has been cancelled
CI / Skills: manifest in sync (push) Has been cancelled
CI / CLI: npx shim (macos-latest) (push) Has been cancelled
CI / CLI: npx shim (ubuntu-latest) (push) Has been cancelled
CI / CLI: npx shim (windows-latest) (push) Has been cancelled
CI / SDK: unit + contract + smoke (push) Has been cancelled
CI / Test: runtime contract (push) Has been cancelled
CI / Studio: load smoke (push) Has been cancelled
CI / Smoke: global install (push) Has been cancelled
CI / CLI smoke (required) (push) Has been cancelled
CI / Semantic PR title (push) Has been cancelled
Player perf / Detect changes (push) Has been cancelled
Player perf / Preflight (lint + format) (push) Has been cancelled
Player perf / player-perf (push) Has been cancelled
Player perf / Perf: drift (push) Has been cancelled
Player perf / Perf: fps (push) Has been cancelled
Player perf / Perf: parity (push) Has been cancelled
Player perf / Perf: scrub (push) Has been cancelled
Player perf / Perf: load (push) Has been cancelled
preview-regression / Detect changes (push) Has been cancelled
preview-regression / Preflight (lint + format) (push) Has been cancelled
preview-regression / Preview parity (push) Has been cancelled
preview-regression / preview-regression (push) Has been cancelled
regression / regression (push) Has been cancelled
regression / Detect changes (push) Has been cancelled
regression / Preflight (lint + format) (push) Has been cancelled
regression / regression-shards (hdr-regression style-5-prod style-3-prod mov-prores, shard-1) (push) Has been cancelled
regression / regression-shards (overlay-montage-prod style-12-prod chat missing-host-comp-id png-sequence portrait-edge-bleed, shard-6) (push) Has been cancelled
regression / regression-shards (style-13-prod style-6-prod vignelli-stacking gsap-letters-render-compat audio-mux-parity, shard-8) (push) Has been cancelled
regression / regression-shards (style-15-prod hdr-hlg-regression style-1-prod many-cuts vfr-screen-recording render-symlinked-assets, shard-2) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:58:35 +08:00

607 lines
20 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Scene — HyperFrames Workflow Approve & Press</title>
<!--
Blueprint: workflow-approve-press
HyperFrames composition.tsx.
Same four-phase choreography, GSAP-driven, seek-safe.
Audio (assumed accompanying narration):
"This is AI that renders with you, not just for you.
Preview the result, render to MP4, then confirm."
Sub-phases (local time, 5.5s total):
0.17 0.72s Headline "AI edits WITH you" slides down
0.50 1.10s Center editor mockup scales in
0.67 2.17s Step indicators stagger-enter on the left (3D-tilted)
2.00 3.33s Step state machine: 1→complete, 2→active … 2→complete, 3→active
3.52 4.02s Confirm MP4 button (3D-tilted right) bouncy entry after render step is active
4.22 4.72s Button depression (linear, no overshoot)
4.72 5.22s Render confirmed: step 3 completes, color shifts to success green + checkmark pop
Asset placeholders:
./assets/editor-demo.mp4 — center product demo video. Replace with a
real export of the AI-editor flow. The
composition falls back to a CSS-mockup of
an editor timeline so it renders without
network or media files.
-->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
:root {
/* Palette — port of the the source COLORS / TYPOGRAPHY constants */
--text-primary: #ffffff;
--text-secondary: #cbd5e1;
--text-muted: #94a3b8;
--brand-purple: #a78bfa;
--brand-purple-glow: rgba(167, 139, 250, 0.6);
--brand-green: #22c55e;
--brand-green-glow: rgba(34, 197, 94, 0.6);
--panel-stroke: rgba(255, 255, 255, 0.1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
margin: 0;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #000;
font-family:
"Inter",
system-ui,
-apple-system,
sans-serif;
color: var(--text-primary);
}
/* ============================================================
BACKGROUND — port of <FrostedGlassBackground />
============================================================ */
.bg {
position: absolute;
inset: 0;
background:
radial-gradient(ellipse at 25% 30%, rgba(167, 139, 250, 0.2), transparent 55%),
radial-gradient(ellipse at 80% 70%, rgba(34, 197, 94, 0.12), transparent 55%),
linear-gradient(135deg, #1a1530 0%, #0a0815 70%);
}
/* Ambient warm glow on the right (button side) */
.ambient {
position: absolute;
inset: 0;
background: radial-gradient(
ellipse at 70% 50%,
var(--brand-purple-glow) 0%,
transparent 40%
);
opacity: 0.15;
pointer-events: none;
}
/* Vignette */
.vignette {
position: absolute;
inset: 0;
background: radial-gradient(ellipse at center, transparent 40%, rgba(0, 0, 0, 0.4) 100%);
pointer-events: none;
}
/* ============================================================
PHASE 1 — HEADLINE
============================================================ */
.headline-wrap {
position: absolute;
top: 80px;
left: 50%;
transform: translateX(-50%);
opacity: 0; /* GSAP fades in */
text-align: center;
white-space: nowrap;
}
.headline {
font-size: 96px;
font-weight: 800;
line-height: 1.2;
letter-spacing: -0.01em;
color: var(--text-primary);
}
.headline .accent {
color: var(--brand-purple);
text-shadow: 0 0 30px var(--brand-purple-glow);
}
/* ============================================================
PHASE 2 — CENTER DEMO
(Falls back to a CSS-mockup editor when ./assets/editor-demo.mp4
is missing. The mockup is purely decorative — no animation
inside it; the eye reads it as a paused product screenshot.)
============================================================ */
.demo-wrap {
position: absolute;
left: 50%;
top: 60%;
opacity: 0;
}
.demo-frame {
width: 1000px;
height: 600px;
border-radius: 16px;
overflow: hidden;
border: 2px solid var(--panel-stroke);
box-shadow:
0 0 40px var(--brand-purple-glow),
0 20px 60px rgba(0, 0, 0, 0.5);
position: relative;
}
.demo-frame video {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
/* CSS-mockup fallback for the editor demo */
.demo-mockup {
position: absolute;
inset: 0;
display: grid;
grid-template-rows: 56px 1fr 100px;
background: linear-gradient(180deg, #1f1b3a 0%, #15112a 100%);
}
.mockup-toolbar {
display: flex;
align-items: center;
gap: 12px;
padding: 0 20px;
background: #15112a;
border-bottom: 1px solid var(--panel-stroke);
}
.mockup-toolbar .dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.18);
}
.mockup-toolbar .dot.r {
background: #ef4444;
}
.mockup-toolbar .dot.y {
background: #eab308;
}
.mockup-toolbar .dot.g {
background: #22c55e;
}
.mockup-toolbar .title {
margin-left: 18px;
color: var(--text-secondary);
font-size: 16px;
font-weight: 500;
}
.mockup-canvas {
position: relative;
padding: 30px;
overflow: hidden;
background:
repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.04) 0 1px, transparent 1px 40px),
repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.04) 0 1px, transparent 1px 40px);
}
.mockup-canvas .preview-box {
width: 100%;
height: 100%;
border-radius: 10px;
background: linear-gradient(135deg, rgba(167, 139, 250, 0.35), rgba(34, 197, 94, 0.2));
border: 1px solid var(--panel-stroke);
box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.4);
display: flex;
align-items: center;
justify-content: center;
color: var(--text-secondary);
font-size: 18px;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
}
.mockup-timeline {
display: flex;
flex-direction: column;
gap: 6px;
padding: 14px 20px;
background: #15112a;
border-top: 1px solid var(--panel-stroke);
}
.mockup-timeline .track {
height: 24px;
display: flex;
gap: 4px;
}
.mockup-timeline .clip {
height: 100%;
border-radius: 4px;
}
.mockup-timeline .t1 .a {
flex: 3;
background: var(--brand-purple);
opacity: 0.85;
}
.mockup-timeline .t1 .b {
flex: 2;
background: var(--brand-purple);
opacity: 0.55;
}
.mockup-timeline .t1 .c {
flex: 4;
background: var(--brand-purple);
opacity: 0.85;
}
.mockup-timeline .t2 .a {
flex: 5;
background: var(--brand-green);
opacity: 0.75;
}
.mockup-timeline .t2 .b {
flex: 2;
background: var(--brand-green);
opacity: 0.45;
}
.mockup-timeline .t2 .c {
flex: 2;
background: var(--brand-green);
opacity: 0.75;
}
/* ============================================================
PHASE 3 — LEFT FLANK: STEP INDICATORS (3D-TILTED)
============================================================ */
.steps-flank {
position: absolute;
left: 100px;
top: 55%;
transform: translateY(-50%) perspective(800px) rotateY(15deg);
display: flex;
flex-direction: column;
gap: 30px;
}
.step {
display: flex;
align-items: center;
gap: 16px;
opacity: 0; /* GSAP fades in */
}
.step-circle {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid var(--text-muted);
background-color: transparent;
box-shadow: none;
transition: none; /* Steps SNAP between states, no CSS transition */
flex-shrink: 0;
}
.step-num {
font-size: 18px;
font-weight: 700;
color: var(--text-muted);
}
.step-check {
display: none;
}
.step-label {
font-size: 28px;
font-weight: 500;
color: var(--text-secondary);
}
/* State machine — toggled via tl.set({ attr: data-state }) */
.step[data-state="active"] .step-circle {
border-color: var(--brand-purple);
box-shadow: 0 0 15px var(--brand-purple-glow);
}
.step[data-state="active"] .step-num {
color: var(--brand-purple);
}
.step[data-state="active"] .step-label {
font-weight: 700;
color: var(--text-primary);
}
.step[data-state="complete"] .step-circle {
border-color: var(--brand-green);
background-color: var(--brand-green);
}
.step[data-state="complete"] .step-num {
display: none;
}
.step[data-state="complete"] .step-check {
display: inline-flex;
}
/* ============================================================
PHASE 4 — RIGHT FLANK: CONFIRM BUTTON (3D-TILTED)
============================================================ */
.button-flank {
position: absolute;
right: 70px;
top: 55%;
transform: translateY(-50%) perspective(800px) rotateY(-15deg);
}
.btn-press {
opacity: 0;
transform-origin: center center;
}
.btn {
--btn-glow-blur: 20px;
padding: 20px 50px;
border-radius: 12px;
background-color: var(--brand-purple);
box-shadow: 0 0 var(--btn-glow-blur) var(--brand-purple-glow);
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
color: var(--text-primary);
font-size: 36px;
font-weight: 700;
white-space: nowrap;
}
.btn-check {
display: inline-flex;
transform-origin: center center;
}
</style>
</head>
<body>
<div
id="root"
data-composition-id="interactive-workflow"
data-start="0"
data-duration="5.5"
data-width="1920"
data-height="1080"
style="position: relative; width: 1920px; height: 1080px; overflow: hidden"
>
<!-- Background layers -->
<div class="bg"></div>
<!-- PHASE 1: Headline -->
<div class="headline-wrap">
<div class="headline">
HyperFrames builds
<span class="accent">WITH</span>
you
</div>
</div>
<!-- PHASE 2: Center demo (video with CSS-mockup fallback) -->
<div class="demo-wrap">
<div class="demo-frame">
<!--
Real asset path (replace for production).
If missing, the CSS mockup below shows through the transparent
poster region. The video stays muted per HyperFrames rules.
-->
<!-- Decorative CSS-mockup of an editor — paused screenshot feel. -->
<div class="demo-mockup">
<div class="mockup-toolbar">
<span class="dot r"></span><span class="dot y"></span><span class="dot g"></span>
<span class="title">HyperFrames — Project: Launch Promo</span>
</div>
<div class="mockup-canvas">
<div class="preview-box">Preview · 1920 × 1080</div>
</div>
<div class="mockup-timeline">
<div class="track t1">
<div class="clip a"></div>
<div class="clip b"></div>
<div class="clip c"></div>
</div>
<div class="track t2">
<div class="clip a"></div>
<div class="clip b"></div>
<div class="clip c"></div>
</div>
</div>
</div>
</div>
</div>
<!-- PHASE 3: Left-flank step indicators (3D-tilted +15°) -->
<div class="steps-flank">
<div class="step step-1" data-step="1" data-state="pending">
<div class="step-circle">
<span class="step-num">1</span>
<svg class="step-check" width="20" height="20" viewBox="0 0 24 24" fill="none">
<path d="M20 6L9 17L4 12" stroke="#fff" stroke-width="2.5" stroke-linecap="round" />
</svg>
</div>
<span class="step-label">Compose HTML Scene</span>
</div>
<div class="step step-2" data-step="2" data-state="pending">
<div class="step-circle">
<span class="step-num">2</span>
<svg class="step-check" width="20" height="20" viewBox="0 0 24 24" fill="none">
<path d="M20 6L9 17L4 12" stroke="#fff" stroke-width="2.5" stroke-linecap="round" />
</svg>
</div>
<span class="step-label">Seek &amp; Preview</span>
</div>
<div class="step step-3" data-step="3" data-state="pending">
<div class="step-circle">
<span class="step-num">3</span>
<svg class="step-check" width="20" height="20" viewBox="0 0 24 24" fill="none">
<path d="M20 6L9 17L4 12" stroke="#fff" stroke-width="2.5" stroke-linecap="round" />
</svg>
</div>
<span class="step-label">Render to MP4</span>
</div>
</div>
<!-- PHASE 4: Right-flank confirm button (3D-tilted -15°) -->
<div class="button-flank">
<div class="btn-press">
<div class="btn">
<span class="btn-check">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none">
<path
d="M20 6L9 17L4 12"
stroke="#fff"
stroke-width="3"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
<span class="btn-label">Confirm MP4</span>
</div>
</div>
</div>
<!-- Ambient + vignette overlays (pointer-events: none) -->
<div class="ambient"></div>
<div class="vignette"></div>
</div>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
// ── Phase boundaries (seconds) — match the blueprint ────────────
const HEADLINE_START = 0.17;
const HEADLINE_END = 0.72;
const VIDEO_START = 0.5;
const STEPS_START = 0.67;
const STEP_STAGGER = 0.5;
const STEP_ACTIVE_T2 = 2.0; // step 1 → complete, step 2 → active
const STEP_ACTIVE_T3 = 3.33; // step 2 → complete, step 3 → active
const BUTTON_ENTER = 3.52; // after "Render to MP4" becomes active
const PRESS_FRAME = 4.22;
const PRESS_DURATION = 0.5;
const CHECK_POP = PRESS_FRAME + PRESS_DURATION;
const SCENE_END = 5.5; // matches data-duration on the root
// ── Phase 1: Headline slides down from top ─────────────────────
gsap.set(".demo-wrap", { xPercent: -50, yPercent: -50, scale: 0.8, opacity: 0 });
gsap.set(".step", { x: -30, opacity: 0 });
gsap.set(".btn-press", { scale: 0, opacity: 0 });
gsap.set(".btn-check", { scale: 0 });
tl.fromTo(
".headline-wrap",
{ y: -40, opacity: 0 },
{
y: 0,
opacity: 1,
duration: HEADLINE_END - HEADLINE_START,
ease: "back.out(1.2)", // ≈ the source SPRING_CONFIGS.entrance
},
HEADLINE_START,
);
// ── Phase 2: Center demo scales in ─────────────────────────────
// HyperFrames syncs the <video> element's currentTime to the seek
// position automatically — no .play() call needed (and one here
// would break deterministic rendering).
tl.to(".demo-wrap", { scale: 1, opacity: 1, duration: 0.6, ease: "power3.out" }, VIDEO_START);
// ── Phase 3a: Stagger entry of the step indicators ─────────────
tl.to(
".step",
{
x: 0,
opacity: 1,
duration: 0.4,
ease: "power3.out",
stagger: { each: STEP_STAGGER, from: "start" },
},
STEPS_START,
);
// ── Phase 3b: Step state machine (snap-toggled here) ──
// Step 1 enters in the "active" state because narration starts on it.
tl.set(".step-1", { attr: { "data-state": "active" } }, STEPS_START);
// First transition — step 1 finishes, step 2 takes over.
tl.set(".step-1", { attr: { "data-state": "complete" } }, STEP_ACTIVE_T2);
tl.set(".step-2", { attr: { "data-state": "active" } }, STEP_ACTIVE_T2);
// Second transition — step 2 finishes, step 3 takes over.
tl.set(".step-2", { attr: { "data-state": "complete" } }, STEP_ACTIVE_T3);
tl.set(".step-3", { attr: { "data-state": "active" } }, STEP_ACTIVE_T3);
// ── Phase 4a: Button bouncy entry ──────────────────────────────
tl.to(
".btn-press",
{ scale: 1, opacity: 1, duration: 0.5, ease: "back.out(1.4)", overwrite: "auto" },
BUTTON_ENTER,
);
// ── Phase 4b: Press (linear depression → linear return) ────────
// Two adjacent tweens on .btn-press — end value of (1) = start value of (2).
tl.to(".btn-press", { scale: 0.95, duration: 0.1, ease: "power1.out" }, PRESS_FRAME);
tl.to(
".btn-press",
{ scale: 1.0, duration: PRESS_DURATION - 0.1, ease: "power1.in" },
PRESS_FRAME + 0.1,
);
// ── Phase 4c: Color shift + label swap (at press end) ──────────
tl.to(
".btn",
{
backgroundColor: "#15803d",
boxShadow: "0 0 25px rgba(21, 128, 61, 0.68)",
duration: 0.3,
ease: "power2.out",
},
CHECK_POP,
);
tl.set(".btn-label", { textContent: "Approved!" }, CHECK_POP);
tl.set(".step-3", { attr: { "data-state": "complete" } }, CHECK_POP);
// ── Phase 4d: Checkmark pops into view ─────────────────────────
// back.out(1.6) ≈ the source spring({ stiffness: 200, damping: 15 }).
tl.to(".btn-check", { scale: 1, duration: 0.5, ease: "back.out(1.6)" }, CHECK_POP);
// ── Ambient pulsing glow on the button (Math.sin) ──
// Source: `glowIntensity = 20 + Math.sin(frame * 0.1) * 10` — i.e. blur
// oscillates 10 ↔ 30 around 20, with angular freq 0.1 rad/frame.
// At 60fps that's 6 rad/s, so full period ≈ 2π / 6 ≈ 1.05s.
// Finite yoyo replaces the continuous sine; `box-shadow` reads the
// tweened CSS variable so the blur is actually animatable.
const PULSE_PERIOD = 1.05;
const PULSE_HALVES = Math.max(2, Math.floor((SCENE_END - BUTTON_ENTER) / (PULSE_PERIOD / 2)));
tl.fromTo(
".btn",
{ "--btn-glow-blur": "10px" }, // source amplitude: 20 ± 10
{
"--btn-glow-blur": "30px",
duration: PULSE_PERIOD / 2,
ease: "sine.inOut",
yoyo: true,
repeat: PULSE_HALVES - 1,
},
BUTTON_ENTER,
);
window.__timelines["interactive-workflow"] = tl;
</script>
</body>
</html>