Files
heygen-com--hyperframes/skills/hyperframes-animation/examples/cta-morph-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

469 lines
15 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 10 — HyperFrames Morph Press Interact</title>
<!--
HyperFrames composition.tsx.
Choreography (4 phases, 5.5 seconds total):
0.17 0.64s Hero "HyperFrames" + logo asset fade up (opacity + y rise)
0.17 5.50s Logo asset rotates ±4° continuously (sine onUpdate)
2.17 2.62s Hero shrinks (1 → 0.6) and fades (1 → 0)
2.17 2.62s "Build video from HTML" CTA pill pops in with back.out(2) overshoot
2.33 2.66s CTA text fades + lifts into place (after container is recognizable)
2.83s Cursor hard-cuts in at off-screen bottom-right
2.83 3.83s Cursor approaches via spring path to (W/2 + 120, H/2 + 50)
3.83 3.98s Click DOWN: both cursor and CTA compress to scale 0.9 (sync)
4.17 4.42s Click UP: both return to scale 1.0 (sync)
DOM + GSAP opacity tweens; z-index keeps incoming above outgoing
- Press synchronization uses single GSAP target array ["#cta", "#cursor"]
to guarantee perfectly identical tween values
- Cursor opacity uses a 0.001-second fromTo for hard-cut step change
- Logo uses a local static image asset referenced with a plain URL
- Breathing rotation only on the logo, not on the whole hero (avoids
conflict with the morph exit scale tween)
-->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
:root {
--hf-bg: #050711;
--hf-bg-2: #0b1020;
--hf-ink: #f8fafc;
--hf-muted: #cbd5e1;
--hf-purple: #7c3aed;
--hf-purple-soft: #a78bfa;
--hf-cyan: #18d9e8;
--hf-green: #22c55e;
--hf-glass: rgba(248, 250, 252, 0.08);
--white: #ffffff;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
margin: 0;
width: 1920px;
height: 1080px;
overflow: hidden;
background:
radial-gradient(ellipse at 50% 42%, rgba(24, 217, 232, 0.14), transparent 50%),
radial-gradient(ellipse at 62% 48%, rgba(124, 58, 237, 0.2), transparent 56%),
radial-gradient(ellipse at 42% 58%, rgba(34, 197, 94, 0.1), transparent 58%),
linear-gradient(135deg, #050711 0%, #090b18 48%, #120717 100%);
font-family:
"Inter",
system-ui,
-apple-system,
sans-serif;
color: var(--hf-ink);
}
.stage {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
}
/* ============================================================
HERO LOCKUP (Phase 1 — exits during morph)
============================================================ */
.hero {
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
gap: 54px;
will-change: transform, opacity;
}
.hero-text {
font-size: 220px;
font-weight: 800;
color: var(--hf-ink);
letter-spacing: 0;
line-height: 1;
margin: 0;
text-shadow:
0 0 34px rgba(167, 139, 250, 0.18),
0 0 72px rgba(24, 217, 232, 0.1);
}
.hero-text .brand-accent {
color: var(--hf-purple-soft);
text-shadow:
0 0 34px rgba(124, 58, 237, 0.45),
0 0 82px rgba(167, 139, 250, 0.22);
}
.hero-logo {
width: 300px;
height: 300px;
margin-left: 8px;
will-change: transform;
display: flex;
align-items: center;
justify-content: center;
}
.hero-logo .logo-mark {
width: 100%;
height: 100%;
display: block;
border-radius: 36px;
box-shadow:
0 0 64px rgba(24, 217, 232, 0.32),
0 0 112px rgba(34, 197, 94, 0.18),
0 0 150px rgba(124, 58, 237, 0.12);
}
/* ============================================================
CTA PILL (Phase 2 — pops in during morph)
============================================================ */
.cta {
background: linear-gradient(
135deg,
rgba(124, 58, 237, 0.96) 0%,
rgba(24, 217, 232, 0.9) 48%,
rgba(34, 197, 94, 0.96) 100%
);
padding: 70px 200px;
border-radius: 200px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
border: 1px solid rgba(24, 217, 232, 0.55);
box-shadow:
0 24px 60px rgba(0, 0, 0, 0.42),
0 0 72px rgba(24, 217, 232, 0.24),
0 0 120px rgba(124, 58, 237, 0.24),
inset 0 1px 0 rgba(255, 255, 255, 0.24),
inset 0 0 34px var(--hf-glass);
will-change: transform, opacity;
/* initial scale 0 + opacity 0 set by gsap.set() */
}
.cta-text {
color: var(--hf-bg);
font-size: 110px;
font-weight: 700;
white-space: nowrap;
text-shadow:
0 1px 0 rgba(255, 255, 255, 0.3),
0 0 22px rgba(255, 255, 255, 0.12);
will-change: transform, opacity;
/* initial opacity 0 + y 10 set by gsap.set() */
}
/* ============================================================
CURSOR (Phase 3 — hard-cuts in)
============================================================ */
.cursor {
position: absolute;
left: 0;
top: 0;
width: 120px;
height: 120px;
will-change: transform, opacity;
/* initial x/y/opacity set by gsap.set() */
}
.cursor .cursor-svg {
width: 100%;
height: 100%;
display: block;
}
</style>
</head>
<body>
<div
id="root"
data-composition-id="main"
data-start="0"
data-duration="5.5"
data-width="1920"
data-height="1080"
>
<div class="stage clip" data-start="0" data-duration="5.5" data-track-index="1" id="stage">
<!-- =====================================================
HERO LOCKUP — "HyperFrames" + logo
===================================================== -->
<div
class="hero clip"
id="hero"
data-start="0"
data-duration="2.62"
data-track-index="2"
data-layout-allow-overflow
aria-hidden="true"
>
<h1 class="hero-text">Hyper<span class="brand-accent">Frames</span></h1>
<div class="hero-logo" id="hero-logo">
<!-- Inline-SVG placeholder mark — swap for your logo image -->
<svg class="logo-mark" viewBox="0 0 100 100" role="img" aria-label="HyperFrames">
<defs>
<linearGradient id="hfMark" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#8b7bff" />
<stop offset="1" stop-color="#3ddc97" />
</linearGradient>
</defs>
<rect x="4" y="4" width="92" height="92" rx="22" fill="url(#hfMark)" />
<text
x="50"
y="63"
text-anchor="middle"
font-family="Inter, system-ui, sans-serif"
font-size="40"
font-weight="800"
fill="#fff"
>
HF
</text>
</svg>
</div>
</div>
<!-- =====================================================
CTA PILL — "Build video from HTML"
===================================================== -->
<div class="cta" id="cta">
<span class="cta-text">Build video from HTML</span>
</div>
<!-- =====================================================
CURSOR — inline SVG arrow with drop-shadow
===================================================== -->
<div class="cursor" id="cursor">
<svg
class="cursor-svg"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<defs>
<filter id="cursorShadow" x="-50%" y="-50%" width="200%" height="200%">
<feDropShadow dx="2" dy="4" stdDeviation="3" flood-opacity="0.3" />
</filter>
</defs>
<path
d="M5.5 3.5L19 10.5L11.5 12.5L15.5 19.5L13.5 20.5L9.5 13.5L5.5 17.5V3.5Z"
fill="#050711"
stroke="#f8fafc"
stroke-width="1.5"
stroke-linejoin="round"
filter="url(#cursorShadow)"
/>
</svg>
</div>
</div>
</div>
<script>
/* ================================================================
TIMING
================================================================ */
const W = 1920,
H = 1080;
const TOTAL_DUR = 5.5;
const TIMING = {
introStart: 0.17, // ~5 frames at 30 fps
introDur: 0.47, // hero enter spring settle
morphAt: 2.17, // ~65 frames
morphExitDur: 0.5,
morphFadeDur: 0.15, // 30% of exit
morphEntDur: 0.45, // CTA pop-in
textRevealAt: 2.33, // morph + 0.17 s
textRevealDur: 0.33,
cursorEnterAt: 2.83,
cursorPathDur: 1.0,
clickDownAt: 3.83,
clickDownDur: 0.15,
clickUpAt: 4.17,
clickUpDur: 0.25,
pressIntensity: 0.1,
};
/* ================================================================
INITIAL STATES (via gsap.set, before the timeline runs)
================================================================ */
// Hero: starts at opacity 0, y +40 (will fade up)
gsap.set("#hero", { opacity: 0, y: 40 });
// CTA: starts invisible (scale 0, opacity 0) — morph pops it in
gsap.set("#cta", { scale: 0, opacity: 0 });
// CTA text: starts at opacity 0 + y 10 — reveals after container is recognizable
gsap.set(".cta-text", { opacity: 0, y: 10 });
// Cursor: starts off-screen bottom-right + invisible + scale 1 baseline
gsap.set("#cursor", {
x: W + 100,
y: H + 200,
scale: 1,
opacity: 0,
});
/* ================================================================
TIMELINE
================================================================ */
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
window.__timelines["main"] = tl;
/* ----------------------------------------------------------------
PHASE 1: Hero entrance + breathing rotation on logo
---------------------------------------------------------------- */
tl.to(
"#hero",
{
opacity: 1,
y: 0,
duration: TIMING.introDur,
ease: "power3.out", // spring(stiffness:120, damping:14)
},
TIMING.introStart,
);
// Continuous breathing rotation on the logo. ±4° over ~6.3 s period.
// For a 5.5 s composition this is just under one cycle — subtle but alive.
// Use Form 2 (onUpdate reading tl.time) per sine-wave-loop rule.
const logoEl = document.querySelector("#hero-logo");
tl.to(
{ tick: 0 },
{
tick: 1,
duration: TOTAL_DUR,
ease: "none",
onUpdate: function () {
const t = tl.time();
gsap.set(logoEl, { rotation: Math.sin(t * 1.0) * 4 });
},
},
0,
);
/* ----------------------------------------------------------------
PHASE 2: Scale-swap morph
Three concurrent tween clusters from MORPH_AT.
---------------------------------------------------------------- */
// (1) Hero shrinks
tl.to(
"#hero",
{
scale: 0.6,
duration: TIMING.morphExitDur,
ease: "power3.out", // spring(stiffness:150, damping:18)
},
TIMING.morphAt,
);
// (2) Hero fades fast (30% of shrink dur)
tl.to(
"#hero",
{
opacity: 0,
duration: TIMING.morphFadeDur,
ease: "power2.out",
},
TIMING.morphAt,
);
tl.set(
"#hero",
{
x: -4000,
},
TIMING.morphAt + TIMING.morphFadeDur,
);
// (3) CTA pops in with overshoot
tl.to(
"#cta",
{
scale: 1,
opacity: 1,
duration: TIMING.morphEntDur,
ease: "back.out(2)", // spring(stiffness:200, damping:15, mass:0.6)
},
TIMING.morphAt,
);
// (4) CTA text reveals after container reaches recognizable scale
tl.to(
".cta-text",
{
opacity: 1,
y: 0,
duration: TIMING.textRevealDur,
ease: "power2.out",
},
TIMING.textRevealAt,
);
/* ----------------------------------------------------------------
PHASE 3: Cursor entry + motion path
---------------------------------------------------------------- */
// Hard-cut opacity (cursors don't fade in — they appear).
// 0.001 s tween creates a step change that scrubs correctly.
tl.fromTo(
"#cursor",
{ opacity: 0 },
{ opacity: 1, duration: 0.001, ease: "none" },
TIMING.cursorEnterAt,
);
// Spring-driven approach to a target slightly offset from center
// (where a human would naturally aim — not dead-center).
tl.to(
"#cursor",
{
x: W / 2 + 120,
y: H / 2 + 50,
duration: TIMING.cursorPathDur,
ease: "power2.out", // spring(stiffness:60, damping:20)
},
TIMING.cursorEnterAt,
);
/* ----------------------------------------------------------------
PHASE 4: Physics-based press
Both CTA and cursor compress together — single target array.
---------------------------------------------------------------- */
// Press DOWN — both elements compress to (1 - intensity)
tl.to(
["#cta", "#cursor"],
{
scale: 1 - TIMING.pressIntensity,
duration: TIMING.clickDownDur,
ease: "power3.out", // spring(stiffness:300, damping:20)
},
TIMING.clickDownAt,
);
// RELEASE — back to scale 1.0
tl.to(
["#cta", "#cursor"],
{
scale: 1.0,
duration: TIMING.clickUpDur,
ease: "power2.out", // spring(stiffness:200, damping:15)
},
TIMING.clickUpAt,
);
</script>
</body>
</html>