Setting the file. One moment. Cta Morph Press · Hyperframes Animation · heygen-com/hyperframes · Skills DocsBlueprints Index
(opens in a new tab)
examples/cta-morph-press.html
HTML·468 lines·15 KB
8 <!--
9 HyperFrames composition.tsx.
10
11 Choreography (4 phases, 5.5 seconds total):
12 0.17 – 0.64s Hero "HyperFrames" + logo asset fade up (opacity + y rise)
13 0.17 – 5.50s Logo asset rotates ±4° continuously (sine onUpdate)
14 2.17 – 2.62s Hero shrinks (1 → 0.6) and fades (1 → 0)
15 2.17 – 2.62s "Build video from HTML" CTA pill pops in with back.out(2) overshoot
16 2.33 – 2.66s CTA text fades + lifts into place (after container is recognizable)
17 2.83s Cursor hard-cuts in at off-screen bottom-right
18 2.83 – 3.83s Cursor approaches via spring path to (W/2 + 120, H/2 + 50)
19 3.83 – 3.98s Click DOWN: both cursor and CTA compress to scale 0.9 (sync)
20 4.17 – 4.42s Click UP: both return to scale 1.0 (sync)
21 DOM + GSAP opacity tweens; z-index keeps incoming above outgoing
22 - Press synchronization uses single GSAP target array ["#cta", "#cursor"]
23 to guarantee perfectly identical tween values
24 - Cursor opacity uses a 0.001-second fromTo for hard-cut step change
25 - Logo uses a local static image asset referenced with a plain URL
26 - Breathing rotation only on the logo, not on the whole hero (avoids
27 conflict with the morph exit scale tween)
28 -->
29
30 <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
31
32 <style>
33 :root {
34 --hf-bg: #050711;
35 --hf-bg-2: #0b1020;
36 --hf-ink: #f8fafc;
37 --hf-muted: #cbd5e1;
38 --hf-purple: #7c3aed;
39 --hf-purple-soft: #a78bfa;
40 --hf-cyan: #18d9e8;
41 --hf-green: #22c55e;
42 --hf-glass: rgba(248, 250, 252, 0.08);
43 --white: #ffffff;
44 }
45 * {
46 margin: 0;
47 padding: 0;
48 box-sizing: border-box;
49 }
50 html,
51 body {
52 margin: 0;
53 width: 1920px;
54 height: 1080px;
55 overflow: hidden;
56 background:
57 radial-gradient(ellipse at 50% 42%, rgba(24, 217, 232, 0.14), transparent 50%),
58 radial-gradient(ellipse at 62% 48%, rgba(124, 58, 237, 0.2), transparent 56%),
59 radial-gradient(ellipse at 42% 58%, rgba(34, 197, 94, 0.1), transparent 58%),
60 linear-gradient(135deg, #050711 0%, #090b18 48%, #120717 100%);
61 font-family:
62 "Inter",
63 system-ui,
64 -apple-system,
65 sans-serif;
66 color: var(--hf-ink);
67 }
68
69 .stage {
70 position: absolute;
71 inset: 0;
72 display: flex;
73 align-items: center;
74 justify-content: center;
75 }
76
77 /* ============================================================
78 HERO LOCKUP (Phase 1 — exits during morph)
79 ============================================================ */
80 .hero {
81 position: absolute;
82 width: 100%;
83 height: 100%;
84 display: flex;
85 align-items: center;
86 justify-content: center;
87 gap: 54px;
88 will-change: transform, opacity;
89 }
90 .hero-text {
91 font-size: 220px;
92 font-weight: 800;
93 color: var(--hf-ink);
94 letter-spacing: 0;
95 line-height: 1;
96 margin: 0;
97 text-shadow:
98 0 0 34px rgba(167, 139, 250, 0.18),
99 0 0 72px rgba(24, 217, 232, 0.1);
100 }
101 .hero-text .brand-accent {
102 color: var(--hf-purple-soft);
103 text-shadow:
104 0 0 34px rgba(124, 58, 237, 0.45),
105 0 0 82px rgba(167, 139, 250, 0.22);
106 }
107 .hero-logo {
108 width: 300px;
109 height: 300px;
110 margin-left: 8px;
111 will-change: transform;
112 display: flex;
113 align-items: center;
114 justify-content: center;
115 }
116 .hero-logo .logo-mark {
117 width: 100%;
118 height: 100%;
119 display: block;
120 border-radius: 36px;
121 box-shadow:
122 0 0 64px rgba(24, 217, 232, 0.32),
123 0 0 112px rgba(34, 197, 94, 0.18),
124 0 0 150px rgba(124, 58, 237, 0.12);
125 }
126
127 /* ============================================================
128 CTA PILL (Phase 2 — pops in during morph)
129 ============================================================ */
130 .cta {
131 background: linear-gradient(
132 135deg,
133 rgba(124, 58, 237, 0.96) 0%,
134 rgba(24, 217, 232, 0.9) 48%,
135 rgba(34, 197, 94, 0.96) 100%
136 );
137 padding: 70px 200px;
138 border-radius: 200px;
139 display: flex;
140 align-items: center;
141 justify-content: center;
142 overflow: hidden;
143 border: 1px solid rgba(24, 217, 232, 0.55);
144 box-shadow:
145 0 24px 60px rgba(0, 0, 0, 0.42),
146 0 0 72px rgba(24, 217, 232, 0.24),
147 0 0 120px rgba(124, 58, 237, 0.24),
148 inset 0 1px 0 rgba(255, 255, 255, 0.24),
149 inset 0 0 34px var(--hf-glass);
150 will-change: transform, opacity;
151 /* initial scale 0 + opacity 0 set by gsap.set() */
152 }
153 .cta-text {
154 color: var(--hf-bg);
155 font-size: 110px;
156 font-weight: 700;
157 white-space: nowrap;
158 text-shadow:
159 0 1px 0 rgba(255, 255, 255, 0.3),
160 0 0 22px rgba(255, 255, 255, 0.12);
161 will-change: transform, opacity;
162 /* initial opacity 0 + y 10 set by gsap.set() */
163 }
164
165 /* ============================================================
166 CURSOR (Phase 3 — hard-cuts in)
167 ============================================================ */
168 .cursor {
169 position: absolute;
170 left: 0;
171 top: 0;
172 width: 120px;
173 height: 120px;
174 will-change: transform, opacity;
175 /* initial x/y/opacity set by gsap.set() */
176 }
177 .cursor .cursor-svg {
178 width: 100%;
179 height: 100%;
180 display: block;
181 }
182 </style>
183 </head>
184 <body>
185 <div
186 id="root"
187 data-composition-id="main"
188 data-start="0"
189 data-duration="5.5"
190 data-width="1920"
191 data-height="1080"
192 >
193 <div class="stage clip" data-start="0" data-duration="5.5" data-track-index="1" id="stage">
194 <!-- =====================================================
195 HERO LOCKUP — "HyperFrames" + logo
196 ===================================================== -->
197 <div
198 class="hero clip"
199 id="hero"
200 data-start="0"
201 data-duration="2.62"
202 data-track-index="2"
203 data-layout-allow-overflow
204 aria-hidden="true"
205 >
206 <h1 class="hero-text">Hyper<span class="brand-accent">Frames</span></h1>
207 <div class="hero-logo" id="hero-logo">
208 <!-- Inline-SVG placeholder mark — swap for your logo image -->
209 <svg class="logo-mark" viewBox="0 0 100 100" role="img" aria-label="HyperFrames">
210 <defs>
211 <linearGradient id="hfMark" x1="0" y1="0" x2="1" y2="1">
212 <stop offset="0" stop-color="#8b7bff" />
213 <stop offset="1" stop-color="#3ddc97" />
214 </linearGradient>
215 </defs>
216 <rect x="4" y="4" width="92" height="92" rx="22" fill="url(#hfMark)" />
217 <text
218 x="50"
219 y="63"
220 text-anchor="middle"
221 font-family="Inter, system-ui, sans-serif"
222 font-size="40"
223 font-weight="800"
224 fill="#fff"
225 >
226 HF
227 </text>
228 </svg>
229 </div>
230 </div>
231
232 <!-- =====================================================
233 CTA PILL — "Build video from HTML"
234 ===================================================== -->
235 <div class="cta" id="cta">
236 <span class="cta-text">Build video from HTML</span>
237 </div>
238
239 <!-- =====================================================
240 CURSOR — inline SVG arrow with drop-shadow
241 ===================================================== -->
242 <div class="cursor" id="cursor">
243 <svg
244 class="cursor-svg"
245 viewBox="0 0 24 24"
246 fill="none"
247 xmlns="http://www.w3.org/2000/svg"
248 aria-hidden="true"
249 >
250 <defs>
251 <filter id="cursorShadow" x="-50%" y="-50%" width="200%" height="200%">
252 <feDropShadow dx="2" dy="4" stdDeviation="3" flood-opacity="0.3" />
253 </filter>
254 </defs>
255 <path
256 d="M5.5 3.5L19 10.5L11.5 12.5L15.5 19.5L13.5 20.5L9.5 13.5L5.5 17.5V3.5Z"
257 fill="#050711"
258 stroke="#f8fafc"
259 stroke-width="1.5"
260 stroke-linejoin="round"
261 filter="url(#cursorShadow)"
262 />
263 </svg>
264 </div>
265 </div>
266 </div>
267
268 <script>
269 /* ================================================================
270 TIMING
271 ================================================================ */
272 const W = 1920,
273 H = 1080;
274 const TOTAL_DUR = 5.5;
275
276 const TIMING = {
277 introStart: 0.17, // ~5 frames at 30 fps
278 introDur: 0.47, // hero enter spring settle
279
280 morphAt: 2.17, // ~65 frames
281 morphExitDur: 0.5,
282 morphFadeDur: 0.15, // 30% of exit
283 morphEntDur: 0.45, // CTA pop-in
284
285 textRevealAt: 2.33, // morph + 0.17 s
286 textRevealDur: 0.33,
287
288 cursorEnterAt: 2.83,
289 cursorPathDur: 1.0,
290
291 clickDownAt: 3.83,
292 clickDownDur: 0.15,
293 clickUpAt: 4.17,
294 clickUpDur: 0.25,
295
296 pressIntensity: 0.1,
297 };
298
299 /* ================================================================
300 INITIAL STATES (via gsap.set, before the timeline runs)
301 ================================================================ */
302 // Hero: starts at opacity 0, y +40 (will fade up)
303 gsap.set("#hero", { opacity: 0, y: 40 });
304
305 // CTA: starts invisible (scale 0, opacity 0) — morph pops it in
306 gsap.set("#cta", { scale: 0, opacity: 0 });
307
308 // CTA text: starts at opacity 0 + y 10 — reveals after container is recognizable
309 gsap.set(".cta-text", { opacity: 0, y: 10 });
310
311 // Cursor: starts off-screen bottom-right + invisible + scale 1 baseline
312 gsap.set("#cursor", {
313 x: W + 100,
314 y: H + 200,
315 scale: 1,
316 opacity: 0,
317 });
318
319 /* ================================================================
320 TIMELINE
321 ================================================================ */
322 window.__timelines = window.__timelines || {};
323 const tl = gsap.timeline({ paused: true });
324 window.__timelines["main"] = tl;
325
326 /* ----------------------------------------------------------------
327 PHASE 1: Hero entrance + breathing rotation on logo
328 ---------------------------------------------------------------- */
329 tl.to(
330 "#hero",
331 {
332 opacity: 1,
333 y: 0,
334 duration: TIMING.introDur,
335 ease: "power3.out", // spring(stiffness:120, damping:14)
336 },
337 TIMING.introStart,
338 );
339
340 // Continuous breathing rotation on the logo. ±4° over ~6.3 s period.
341 // For a 5.5 s composition this is just under one cycle — subtle but alive.
342 // Use Form 2 (onUpdate reading tl.time) per sine-wave-loop rule.
343 const logoEl = document.querySelector("#hero-logo");
344 tl.to(
345 { tick: 0 },
346 {
347 tick: 1,
348 duration: TOTAL_DUR,
349 ease: "none",
350 onUpdate: function () {
351 const t = tl.time();
352 gsap.set(logoEl, { rotation: Math.sin(t * 1.0) * 4 });
353 },
354 },
355 0,
356 );
357
358 /* ----------------------------------------------------------------
359 PHASE 2: Scale-swap morph
360 Three concurrent tween clusters from MORPH_AT.
361 ---------------------------------------------------------------- */
362 // (1) Hero shrinks
363 tl.to(
364 "#hero",
365 {
366 scale: 0.6,
367 duration: TIMING.morphExitDur,
368 ease: "power3.out", // spring(stiffness:150, damping:18)
369 },
370 TIMING.morphAt,
371 );
372
373 // (2) Hero fades fast (30% of shrink dur)
374 tl.to(
375 "#hero",
376 {
377 opacity: 0,
378 duration: TIMING.morphFadeDur,
379 ease: "power2.out",
380 },
381 TIMING.morphAt,
382 );
383
384 tl.set(
385 "#hero",
386 {
387 x: -4000,
388 },
389 TIMING.morphAt + TIMING.morphFadeDur,
390 );
391
392 // (3) CTA pops in with overshoot
393 tl.to(
394 "#cta",
395 {
396 scale: 1,
397 opacity: 1,
398 duration: TIMING.morphEntDur,
399 ease: "back.out(2)", // spring(stiffness:200, damping:15, mass:0.6)
400 },
401 TIMING.morphAt,
402 );
403
404 // (4) CTA text reveals after container reaches recognizable scale
405 tl.to(
406 ".cta-text",
407 {
408 opacity: 1,
409 y: 0,
410 duration: TIMING.textRevealDur,
411 ease: "power2.out",
412 },
413 TIMING.textRevealAt,
414 );
415
416 /* ----------------------------------------------------------------
417 PHASE 3: Cursor entry + motion path
418 ---------------------------------------------------------------- */
419 // Hard-cut opacity (cursors don't fade in — they appear).
420 // 0.001 s tween creates a step change that scrubs correctly.
421 tl.fromTo(
422 "#cursor",
423 { opacity: 0 },
424 { opacity: 1, duration: 0.001, ease: "none" },
425 TIMING.cursorEnterAt,
426 );
427
428 // Spring-driven approach to a target slightly offset from center
429 // (where a human would naturally aim — not dead-center).
430 tl.to(
431 "#cursor",
432 {
433 x: W / 2 + 120,
434 y: H / 2 + 50,
435 duration: TIMING.cursorPathDur,
436 ease: "power2.out", // spring(stiffness:60, damping:20)
437 },
438 TIMING.cursorEnterAt,
439 );
440
441 /* ----------------------------------------------------------------
442 PHASE 4: Physics-based press
443 Both CTA and cursor compress together — single target array.
444 ---------------------------------------------------------------- */
445 // Press DOWN — both elements compress to (1 - intensity)
446 tl.to(
447 ["#cta", "#cursor"],
448 {
449 scale: 1 - TIMING.pressIntensity,
450 duration: TIMING.clickDownDur,
451 ease: "power3.out", // spring(stiffness:300, damping:20)
452 },
453 TIMING.clickDownAt,
454 );
455
456 // RELEASE — back to scale 1.0
457 tl.to(
458 ["#cta", "#cursor"],
459 {
460 scale: 1.0,
461 duration: TIMING.clickUpDur,
462 ease: "power2.out", // spring(stiffness:200, damping:15)
463 },
464 TIMING.clickUpAt,
465 );
466 </script>
467 </body>
468</html>