返回 html-video
stats.html
1 <template id="stats-template">
2 <div
3 data-composition-id="stats"
4 data-width="1920"
5 data-height="1080"
6 data-duration="__VIDEO_DURATION__"
7 >
8 <div id="stats-container">
9 <!-- Moment 1: 47% NEED MOTION GRAPHICS -->
10 <div id="moment-1" class="moment" style="opacity: 0; transform: scale(0) rotate(8deg)">
11 <div class="sticker-stack">
12 <div class="sticker-bg blue-bg"></div>
13 <div class="sticker-content white-bg blue-border">
14 <div class="stat-number blue-text">47%</div>
15 <div class="stat-label blue-text">NEED MOTION GRAPHICS</div>
16 </div>
17 </div>
18 <!-- Decorative shapes -->
19 <div
20 class="shape circle pink-bg"
21 style="top: -40px; left: -40px; width: 80px; height: 80px"
22 ></div>
23 <div
24 class="shape pill lime-bg"
25 style="bottom: -30px; right: -50px; width: 120px; height: 40px; transform: rotate(-15deg)"
26 ></div>
27 </div>
28
29 <!-- Moment 2: 62% STRUGGLE WITH STATIC CONTENT -->
30 <div id="moment-2" class="moment" style="opacity: 0; transform: scale(0) rotate(-5deg)">
31 <div class="sticker-stack">
32 <div class="sticker-bg white-bg"></div>
33 <div class="sticker-content blue-bg white-border">
34 <div class="stat-number white-text">62%</div>
35 <div class="stat-label white-text">STRUGGLE WITH STATIC CONTENT</div>
36 </div>
37 </div>
38 <!-- Decorative shapes -->
39 <div
40 class="shape blob yellow-bg"
41 style="
42 top: -50px;
43 right: -30px;
44 width: 100px;
45 height: 100px;
46 border-radius: 40% 60% 70% 30% / 40% 50% 60% 50%;
47 "
48 ></div>
49 <div
50 class="shape circle pink-bg"
51 style="bottom: -20px; left: -40px; width: 60px; height: 60px"
52 ></div>
53 </div>
54
55 <!-- Moment 3: 75% LACK EDITING SKILLS -->
56 <div id="moment-3" class="moment" style="opacity: 0; transform: scale(0) rotate(3deg)">
57 <div class="sticker-stack">
58 <div class="sticker-bg pink-bg"></div>
59 <div class="sticker-content white-bg pink-border">
60 <div class="stat-number pink-text">75%</div>
61 <div class="stat-label pink-text">LACK EDITING SKILLS</div>
62 </div>
63 </div>
64 <!-- Decorative shapes -->
65 <div
66 class="shape pill blue-bg"
67 style="
68 top: -30px;
69 left: 50%;
70 transform: translateX(-50%) rotate(5deg);
71 width: 150px;
72 height: 45px;
73 "
74 ></div>
75 <div
76 class="shape circle yellow-bg"
77 style="bottom: -40px; right: -20px; width: 90px; height: 90px"
78 ></div>
79 </div>
80 </div>
81
82 <style>
83 [data-composition-id="stats"] {
84 width: 1920px;
85 height: 1080px;
86 position: relative;
87 font-family: "Nunito", sans-serif;
88 overflow: hidden;
89 }
90
91 [data-composition-id="stats"] #stats-container {
92 width: 100%;
93 height: 100%;
94 position: relative;
95 }
96
97 [data-composition-id="stats"] .moment {
98 position: absolute;
99 display: flex;
100 flex-direction: column;
101 align-items: center;
102 justify-content: center;
103 padding: 40px;
104 z-index: 100;
105 }
106
107 /* Layout Positions */
108 [data-composition-id="stats"] #moment-1 {
109 top: 200px;
110 right: 150px; /* Right side when A-roll is Left */
111 width: 600px;
112 }
113
114 [data-composition-id="stats"] #moment-2 {
115 top: 250px;
116 left: 100px; /* Left side when A-roll is Right */
117 width: 700px;
118 }
119
120 [data-composition-id="stats"] #moment-3 {
121 bottom: 100px; /* Below A-roll when centered */
122 left: 50%;
123 transform: translateX(-50%);
124 width: 800px;
125 }
126
127 /* Sticker Stack Effect */
128 [data-composition-id="stats"] .sticker-stack {
129 position: relative;
130 width: 100%;
131 padding: 40px;
132 filter: drop-shadow(0 15px 0 rgba(0, 87, 255, 0.2));
133 }
134
135 [data-composition-id="stats"] .sticker-bg {
136 position: absolute;
137 top: 0;
138 left: 0;
139 width: 100%;
140 height: 100%;
141 border-radius: 40px;
142 z-index: -1;
143 transform: rotate(-2deg);
144 }
145
146 [data-composition-id="stats"] .sticker-content {
147 position: relative;
148 padding: 40px;
149 border-radius: 35px;
150 border: 8px solid currentColor;
151 display: flex;
152 flex-direction: column;
153 align-items: center;
154 text-align: center;
155 }
156
157 /* Colors */
158 [data-composition-id="stats"] .blue-bg {
159 background-color: #0057ff;
160 }
161 [data-composition-id="stats"] .pink-bg {
162 background-color: #ff2d8a;
163 }
164 [data-composition-id="stats"] .lime-bg {
165 background-color: #7fff00;
166 }
167 [data-composition-id="stats"] .yellow-bg {
168 background-color: #ffe500;
169 }
170 [data-composition-id="stats"] .white-bg {
171 background-color: #ffffff;
172 }
173
174 [data-composition-id="stats"] .blue-text {
175 color: #0057ff;
176 }
177 [data-composition-id="stats"] .white-text {
178 color: #ffffff;
179 }
180 [data-composition-id="stats"] .pink-text {
181 color: #ff2d8a;
182 }
183
184 [data-composition-id="stats"] .blue-border {
185 border-color: #0057ff;
186 }
187 [data-composition-id="stats"] .white-border {
188 border-color: #ffffff;
189 }
190 [data-composition-id="stats"] .pink-border {
191 border-color: #ff2d8a;
192 }
193
194 /* Typography */
195 [data-composition-id="stats"] .stat-number {
196 font-size: 180px;
197 font-weight: 900;
198 line-height: 1;
199 margin-bottom: 10px;
200 }
201
202 [data-composition-id="stats"] .stat-label {
203 font-size: 48px;
204 font-weight: 900;
205 line-height: 1.1;
206 max-width: 500px;
207 }
208
209 /* Shapes */
210 [data-composition-id="stats"] .shape {
211 position: absolute;
212 z-index: -2;
213 border-radius: 50%;
214 }
215 [data-composition-id="stats"] .pill {
216 border-radius: 100px;
217 }
218 </style>
219
220 <script>
221 (function () {
222 const TRANSCRIPT = [
223 { text: "We", start: 0.119, end: 0.259 },
224 { text: "asked", start: 0.319, end: 0.479 },
225 { text: "what", start: 0.519, end: 0.659 },
226 { text: "you", start: 0.699, end: 0.819 },
227 { text: "needed.", start: 0.859, end: 1.819 },
228 { text: "Forty-seven", start: 1.839, end: 2.299 },
229 { text: "percent", start: 2.399, end: 2.679 },
230 { text: "of", start: 2.7, end: 2.799 },
231 { text: "you", start: 2.839, end: 2.939 },
232 { text: "said", start: 3.039, end: 3.179 },
233 { text: "motion", start: 3.24, end: 3.559 },
234 { text: "graphics.", start: 3.579, end: 4.639 },
235 { text: "Sixty-two", start: 4.659, end: 5.179 },
236 { text: "percent", start: 5.299, end: 5.759 },
237 { text: "said", start: 5.859, end: 5.98 },
238 { text: "static", start: 6.099, end: 6.399 },
239 { text: "content", start: 6.46, end: 6.879 },
240 { text: "was", start: 6.92, end: 7.079 },
241 { text: "costing", start: 7.099, end: 7.48 },
242 { text: "you", start: 7.5, end: 7.579 },
243 { text: "attention,", start: 7.679, end: 8.659 },
244 { text: "and", start: 8.699, end: 8.86 },
245 { text: "three", start: 8.88, end: 9.06 },
246 { text: "out", start: 9.079, end: 9.18 },
247 { text: "of", start: 9.199, end: 9.34 },
248 { text: "four", start: 9.38, end: 9.799 },
249 { text: "said", start: 9.84, end: 10.0 },
250 { text: "you", start: 10.019, end: 10.159 },
251 { text: "know", start: 10.179, end: 10.36 },
252 { text: "the", start: 10.38, end: 10.479 },
253 { text: "look", start: 10.52, end: 10.699 },
254 { text: "you", start: 10.739, end: 10.859 },
255 { text: "want", start: 10.98, end: 11.34 },
256 { text: "but", start: 11.359, end: 11.52 },
257 { text: "don't", start: 11.56, end: 11.779 },
258 { text: "have", start: 11.819, end: 11.94 },
259 { text: "the", start: 11.96, end: 12.06 },
260 { text: "editing", start: 12.079, end: 12.4 },
261 { text: "skills", start: 12.52, end: 12.86 },
262 { text: "to", start: 12.88, end: 13.0 },
263 { text: "get", start: 13.019, end: 13.18 },
264 { text: "there.", start: 13.22, end: 14.22 },
265 { text: "So", start: 14.239, end: 14.399 },
266 { text: "we", start: 14.42, end: 14.52 },
267 { text: "built", start: 14.619, end: 14.88 },
268 { text: "Hyperframes", start: 15.079, end: 15.42 },
269 ];
270 const tl = gsap.timeline({ paused: true });
271
272 // Helper to find word timing
273 function getWordTime(wordText) {
274 const word = TRANSCRIPT.find((w) =>
275 w.text
276 .toLowerCase()
277 .replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, "")
278 .startsWith(wordText.toLowerCase()),
279 );
280 return word ? word.start : null;
281 }
282
283 // Timings from transcript
284 const t1 = getWordTime("Forty-seven") || 1.839;
285 const t2 = getWordTime("Sixty-two") || 4.659;
286 const t3 = getWordTime("three") || 8.88;
287
288 // Moment 1 Animation (approx 5s)
289 tl.to(
290 "#moment-1",
291 {
292 opacity: 1,
293 scale: 1,
294 duration: 0.8,
295 ease: "elastic.out(1, 0.6)",
296 onStart: () => gsap.set("#moment-1", { transformOrigin: "center center" }),
297 },
298 t1,
299 );
300
301 // Moment 1 Exit (before Moment 2)
302 tl.to(
303 "#moment-1",
304 {
305 opacity: 0,
306 scale: 0.5,
307 duration: 0.3,
308 ease: "back.in(1.7)",
309 },
310 t2 - 0.4,
311 );
312
313 // Moment 2 Animation (approx 9s)
314 tl.to(
315 "#moment-2",
316 {
317 opacity: 1,
318 scale: 1,
319 duration: 0.8,
320 ease: "elastic.out(1, 0.6)",
321 onStart: () => gsap.set("#moment-2", { transformOrigin: "center center" }),
322 },
323 t2,
324 );
325
326 // Moment 2 Exit (before Moment 3)
327 tl.to(
328 "#moment-2",
329 {
330 opacity: 0,
331 scale: 0.5,
332 duration: 0.3,
333 ease: "back.in(1.7)",
334 },
335 t3 - 0.4,
336 );
337
338 // Moment 3 Animation (approx 13s)
339 tl.to(
340 "#moment-3",
341 {
342 opacity: 1,
343 scale: 1,
344 duration: 0.8,
345 ease: "elastic.out(1, 0.6)",
346 onStart: () => gsap.set("#moment-3", { transformOrigin: "center center" }),
347 },
348 t3,
349 );
350
351 // Moment 3 Exit: 3 words before 'Hyperframes'
352 // 'Editor' is at 15.079. 3 words before is 'So' at 14.239.
353 tl.to(
354 "#moment-3",
355 {
356 opacity: 0,
357 scale: 0.5,
358 duration: 0.3,
359 ease: "back.in(1.7)",
360 },
361 14.239,
362 );
363
364 // Ambient Motion (Finite)
365 const totalDuration = 16.04;
366 gsap.utils.toArray(".shape").forEach((shape, i) => {
367 const stepDuration = 2 + i * 0.2;
368 const steps = Math.ceil(totalDuration / stepDuration);
369 for (let s = 0; s < steps; s++) {
370 tl.to(
371 shape,
372 {
373 y: s % 2 === 0 ? 15 : -15,
374 rotation: s % 2 === 0 ? 5 : -5,
375 duration: stepDuration,
376 ease: "sine.inOut",
377 },
378 s * stepDuration,
379 );
380 }
381 });
382
383 window.__timelines = window.__timelines || {};
384 window.__timelines["stats"] = tl;
385 })();
386 </script>
387 </div>
388 </template>
389
389 lines HTML