Setting the file. One moment. Takeover Ticker Displace · Hyperframes Animation · heygen-com/hyperframes · Skills DocsBlueprints Index
(opens in a new tab)
examples/takeover-ticker-displace.html
HTML·347 lines·11 KB
8 <!--
9 HyperFrames composition.tsx.
10
11 Choreography (4 phases, 7.5 seconds total):
12 0.00 – 0.67s Typewriter reveals "HyperFrames turns" (3 → 17 chars)
13 1.67 – 2.22s Ticker scrolls: HTML → motion
14 3.33 – 3.88s Ticker scrolls: motion → video
15 4.60 – 5.45s Logo enters from offscreen-right with rotation + scale impact
16 Text group pushed left + fades (40-50% of hero duration)
17 6.60 – 7.50s Logo breathes (dual-frequency sine onUpdate, multiplicative on 1.3 scale)
18 tuned to 40-50% of hero duration so the impact reads as causal
19 - Breathing uses Form 2 (onUpdate) so it multiplies onto the 1.3 final scale,
20 not overwrites it (Form 1 yoyo would undo the impact)
21 - Two breathing periods (1.0s scale, 1.33s rotation) for organic feel
22 - Logo uses a local static image asset referenced with a plain URL
23 -->
24
25 <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
26
27 <style>
28 :root {
29 --bg: linear-gradient(
30 135deg,
31 #3a3a3a 0%,
32 #17211f 30%,
33 #0b2328 48%,
34 #1f3518 70%,
35 #343434 100%
36 );
37 --text: #f8fafc;
38 --accent: #18d9e8;
39 --accent-2: #7bea5a;
40 }
41 * {
42 margin: 0;
43 padding: 0;
44 box-sizing: border-box;
45 }
46 html,
47 body {
48 margin: 0;
49 width: 1920px;
50 height: 1080px;
51 overflow: hidden;
52 background: var(--bg);
53 font-family:
54 "Inter",
55 system-ui,
56 -apple-system,
57 sans-serif;
58 color: var(--text);
59 }
60
61 .stage {
62 position: absolute;
63 inset: 0;
64 display: flex;
65 align-items: center;
66 justify-content: center;
67 overflow: hidden;
68 }
69
70 /* Text group — typewriter + ticker side by side, displaced as a unit */
71 .text-group {
72 position: absolute;
73 display: flex;
74 flex-direction: row;
75 align-items: center;
76 gap: 20px;
77 }
78
79 .typewriter {
80 height: 168px; /* = ITEM_HEIGHT (FONT_SIZE × 1.2) */
81 display: flex;
82 align-items: center;
83 color: var(--text);
84 font-weight: 400;
85 font-size: 140px;
86 line-height: 1;
87 white-space: pre;
88 text-shadow: 0 0 36px rgba(24, 217, 232, 0.18);
89 }
90
91 /* Vertical ticker */
92 .ticker-window {
93 height: 168px;
94 overflow: hidden;
95 display: inline-flex;
96 flex-direction: column;
97 align-items: flex-start;
98 }
99 .ticker-stack {
100 display: flex;
101 flex-direction: column;
102 will-change: transform;
103 }
104 .ticker-item {
105 height: 168px;
106 display: flex;
107 align-items: center;
108 color: var(--accent);
109 background: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%);
110 -webkit-background-clip: text;
111 background-clip: text;
112 -webkit-text-fill-color: transparent;
113 font-weight: 700;
114 font-size: 140px;
115 padding-left: 20px;
116 line-height: 1;
117 white-space: pre;
118 }
119
120 /* Hero logo — enters from offscreen right */
121 .hero {
122 position: absolute;
123 width: 440px;
124 height: 440px;
125 display: flex;
126 align-items: center;
127 justify-content: center;
128 z-index: 20; /* above text during overlap */
129 opacity: 0; /* fromTo will animate */
130 }
131 .hero .logo-mark {
132 width: 100%;
133 height: 100%;
134 display: block;
135 border-radius: 52px;
136 box-shadow:
137 0 0 70px rgba(24, 217, 232, 0.28),
138 0 0 120px rgba(123, 234, 90, 0.18);
139 }
140 </style>
141 </head>
142 <body>
143 <div
144 id="root"
145 data-composition-id="main"
146 data-start="0"
147 data-duration="7.5"
148 data-width="1920"
149 data-height="1080"
150 >
151 <div
152 class="stage clip"
153 data-start="0"
154 data-duration="7.5"
155 data-track-index="1"
156 id="displace-stage"
157 >
158 <!-- Text group: typewriter + ticker -->
159 <div class="text-group" id="text-group">
160 <div class="typewriter">
161 <span class="typewriter-text">Hyp</span>
162 </div>
163 <div class="ticker-window">
164 <div class="ticker-stack">
165 <div class="ticker-item">HTML</div>
166 <div class="ticker-item">motion</div>
167 <div class="ticker-item">video</div>
168 </div>
169 </div>
170 </div>
171
172 <!-- Hero logo asset -->
173 <div class="hero" id="hero-logo">
174 <!-- Inline-SVG placeholder mark — swap for your logo image -->
175 <svg class="logo-mark" viewBox="0 0 100 100" role="img" aria-label="HyperFrames">
176 <defs>
177 <linearGradient id="hfMark" x1="0" y1="0" x2="1" y2="1">
178 <stop offset="0" stop-color="#8b7bff" />
179 <stop offset="1" stop-color="#3ddc97" />
180 </linearGradient>
181 </defs>
182 <rect x="4" y="4" width="92" height="92" rx="22" fill="url(#hfMark)" />
183 <text
184 x="50"
185 y="63"
186 text-anchor="middle"
187 font-family="Inter, system-ui, sans-serif"
188 font-size="40"
189 font-weight="800"
190 fill="#fff"
191 >
192 HF
193 </text>
194 </svg>
195 </div>
196 </div>
197 </div>
198
199 <script>
200 /* ================================================================
201 CONSTANTS
202 ================================================================ */
203 const TOTAL_DURATION = 7.5;
204 const FONT_SIZE = 140;
205 const ITEM_HEIGHT = FONT_SIZE * 1.2; // = 168 px
206
207 const TIMING = {
208 // Phase 1: typewriter
209 typeStart: 0.0,
210 typeDur: 0.67,
211 typeStartLen: 3, // pre-render the first 3 chars
212
213 // Phase 2: ticker steps
214 ticker1At: 1.67, // HTML → motion
215 ticker2At: 3.33, // motion → video
216 stepDur: 0.55,
217
218 // Phase 3: reactive displacement
219 displaceAt: 4.6,
220 heroDur: 0.85, // matches spring(mass:1.5) settle
221 offscreenX: 800, // hero starts here
222 pushDist: -150, // text pushed THIS direction
223
224 // Phase 4: breathing
225 idleStart: 6.6, // displaceAt + heroDur + ~1.15s buffer
226 };
227
228 const FULL_TEXT = "HyperFrames turns";
229
230 /* ================================================================
231 TIMELINE
232 ================================================================ */
233 window.__timelines = window.__timelines || {};
234 const tl = gsap.timeline({ paused: true });
235 window.__timelines["main"] = tl;
236
237 /* ----------------------------------------------------------------
238 PHASE 1: Typewriter (smooth slice)
239 ---------------------------------------------------------------- */
240 const typewriterEl = document.querySelector(".typewriter-text");
241 const typeProxy = { progress: TIMING.typeStartLen };
242
243 tl.to(
244 typeProxy,
245 {
246 progress: FULL_TEXT.length,
247 duration: TIMING.typeDur,
248 ease: "none",
249 onUpdate: () => {
250 const len = Math.floor(typeProxy.progress);
251 const next = FULL_TEXT.slice(0, len);
252 if (typewriterEl.textContent !== next) typewriterEl.textContent = next;
253 },
254 },
255 TIMING.typeStart,
256 );
257
258 /* ----------------------------------------------------------------
259 PHASE 2: Vertical ticker (2 steps: HTML → motion → video)
260 ---------------------------------------------------------------- */
261 tl.to(
262 ".ticker-stack",
263 {
264 y: `-=${ITEM_HEIGHT}`,
265 duration: TIMING.stepDur,
266 ease: "back.out(1.4)", // spring(stiffness:120, damping:14)
267 },
268 TIMING.ticker1At,
269 );
270
271 tl.to(
272 ".ticker-stack",
273 {
274 y: `-=${ITEM_HEIGHT}`,
275 duration: TIMING.stepDur,
276 ease: "back.out(1.4)",
277 },
278 TIMING.ticker2At,
279 );
280
281 /* ----------------------------------------------------------------
282 PHASE 3: Reactive displacement — three concurrent tweens.
283 ---------------------------------------------------------------- */
284 // (1) Hero enters with rotation + scale impact, lands at scale 1.3.
285 tl.fromTo(
286 ".hero",
287 { x: TIMING.offscreenX, scale: 0.5, rotation: -45, opacity: 0 },
288 {
289 x: 0,
290 scale: 1.3,
291 rotation: 0,
292 opacity: 1,
293 duration: TIMING.heroDur,
294 ease: "power2.out", // spring(stiffness:100, damping:20, mass:1.5)
295 },
296 TIMING.displaceAt,
297 );
298
299 // (2) Text pushed left. Completes at 50% of hero duration → immediate impact.
300 tl.to(
301 ".text-group",
302 { x: TIMING.pushDist, duration: TIMING.heroDur * 0.5, ease: "power2.out" },
303 TIMING.displaceAt,
304 );
305
306 // (3) Text fades. Completes at 40% → fades slightly before push lands.
307 tl.to(
308 ".text-group",
309 { opacity: 0, duration: TIMING.heroDur * 0.4, ease: "power2.out" },
310 TIMING.displaceAt,
311 );
312
313 /* ----------------------------------------------------------------
314 PHASE 4: Breathing — onUpdate so it MULTIPLIES onto the 1.3 scale.
315 Dual frequencies (1.0s scale, 1.33s rotation) for organic motion.
316 ---------------------------------------------------------------- */
317 const heroEl = document.querySelector(".hero");
318 const HERO_FINAL_SCALE = 1.3;
319 const HERO_FINAL_ROTATION = 0;
320 const SCALE_PERIOD = 1.0; // seconds per scale cycle
321 const ROTATE_PERIOD = 1.333; // seconds per rotation cycle — not a simple ratio of SCALE_PERIOD
322 const SCALE_AMP = 0.05;
323 const ROTATE_AMP = 3;
324
325 const breathDur = TOTAL_DURATION - TIMING.idleStart;
326
327 tl.to(
328 { tick: 0 },
329 {
330 tick: 1,
331 duration: breathDur,
332 ease: "none",
333 onUpdate: function () {
334 const idleTime = Math.max(0, tl.time() - TIMING.idleStart);
335 const omegaS = (idleTime / SCALE_PERIOD) * Math.PI * 2;
336 const omegaR = (idleTime / ROTATE_PERIOD) * Math.PI * 2;
337 gsap.set(heroEl, {
338 scale: HERO_FINAL_SCALE * (1 + Math.sin(omegaS) * SCALE_AMP),
339 rotation: HERO_FINAL_ROTATION + Math.sin(omegaR) * ROTATE_AMP,
340 });
341 },
342 },
343 TIMING.idleStart,
344 );
345 </script>
346 </body>
347</html>