GuglielmoTor commited on
Commit
fc169c4
·
verified ·
1 Parent(s): 51b5851

Update sync_animation.html

Browse files
Files changed (1) hide show
  1. sync_animation.html +126 -212
sync_animation.html CHANGED
@@ -5,89 +5,73 @@
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>CMD Ingaze Reveal Animation</title>
7
  <style>
 
8
  body {
9
- background-color: #000000; /* Black background */
10
  color: #00FF00; /* Bright green text */
11
  font-family: 'Courier New', Courier, monospace; /* Classic CMD font */
12
- display: flex;
13
- justify-content: center;
14
- align-items: center;
15
- height: 100vh; /* Make body fill viewport height */
16
- margin: 0;
17
- overflow: hidden; /* Prevent scrollbars */
 
18
  }
 
19
  #cmd-animation-container {
20
- padding: 20px;
21
  border: 2px solid #00AA00; /* Darker green border */
22
  background-color: #080808; /* Slightly off-black for the "screen" */
23
- box-shadow: 0 0 15px #00FF00; /* Green glow effect */
24
  border-radius: 8px; /* Rounded corners for the container */
25
- /* Ensure the container doesn't exceed viewport width if content is too wide */
26
- max-width: 95vw;
27
  box-sizing: border-box; /* Include padding and border in the element's total width and height */
 
 
 
28
  }
 
29
  pre#cmd-animation {
30
  white-space: pre; /* Preserve whitespace and newlines */
31
  text-align: left;
32
- font-size: 14px; /* Adjusted for better fit */
33
- line-height: 1.1; /* Compact lines */
34
  margin: 0;
35
  overflow-x: auto; /* Add scroll if content is wider than container */
 
36
  }
37
  </style>
38
  </head>
39
  <body>
40
  <div id="cmd-animation-container">
41
- <pre id="cmd-animation"></pre>
42
  </div>
43
 
44
  <script>
45
  // --- Configuration ---
46
- const scene_width = 65; // Characters, increased for Ingaze logo
47
  const scene_height = 22; // Lines
48
  const frameDuration = 150; // Milliseconds per frame
49
- const MAX_DATA_PARTICLES = 150;
50
  const FLOOR_LEVEL = scene_height - 1;
51
 
52
  // --- ASCII Art Definitions ---
53
  const stick_man_art_normal = [" O ", " /|\\ ", " / \\ "];
54
  const stick_man_art_walk_1 = [" O ", " /|\\ ", " / > "];
55
  const stick_man_art_walk_2 = [" O ", " /|\\ ", " < \\ "];
56
- const stick_man_art_surprise = [" \\O/ ", " | ", " / \\ "]; // Stickman surprised by lightning/explosion
57
- const stick_man_art_gone = [" ", " ", " "]; // For when stickman disappears
58
 
59
  const linked_in_art = ["+----+", "|i n |", "+----+"];
60
- const linked_in_art_hit = ["x----x", "|BOOM|", "x----x"]; // When hit by lightning
61
 
62
- const lightning_strike_segment = "\\"; // Simple segment for descending lightning
63
 
64
- const explosion_symbols = ['*', '#', '@', '!', '%', '&', 'X', 'O', '$', '+', '~'];
65
  const explosion_art_frames = [
66
- // Frame 1: Small impact
67
- [
68
- " * ",
69
- " *#* ",
70
- " * "
71
- ],
72
- // Frame 2: Expanding
73
- [
74
- " @!% ",
75
- " @*#*& ",
76
- " X%O "
77
- ],
78
- // Frame 3: Larger and more chaotic
79
- [
80
- " $*~+@ ",
81
- "$#@!%*&",
82
- " X*O#~ ",
83
- " !+%$ "
84
- ],
85
- // Frame 4: Fading / contracting
86
- [
87
- " ~ ",
88
- " +*% ",
89
- " $ "
90
- ]
91
  ];
92
 
93
  const ingaze_logo_art = [
@@ -100,10 +84,9 @@
100
  const ingaze_logo_width = ingaze_logo_art[0].length;
101
  const ingaze_logo_height = ingaze_logo_art.length;
102
 
103
-
104
- // --- Global State ---
105
- let data_particles = []; // {char, x, y, vy, active}
106
- let current_lightning_y = -1; // Y position of the lightning tip, -1 means not active
107
 
108
  // --- Helper Functions ---
109
  function overlay(baseLines, artLines, startX, startY, ignoreSpaces = true) {
@@ -123,12 +106,12 @@
123
  });
124
  }
125
 
126
- function drawLightningBolt(sceneLines, tipX, tipY, length) {
127
  for (let i = 0; i < length; i++) {
128
  const y = tipY - i;
129
- const x = tipX + (i % 2 === 0 ? 0 : (i % 4 === 1 ? -1 : 1)); // Simple zig-zag
130
  if (y >= 0 && y < scene_height && x >= 0 && x < scene_width) {
131
- if (sceneLines[y][x] === ' ') { // Avoid overwriting other elements if possible
132
  let lineArr = sceneLines[y].split('');
133
  lineArr[x] = lightning_strike_segment;
134
  sceneLines[y] = lineArr.join('');
@@ -137,15 +120,13 @@
137
  }
138
  }
139
 
140
-
141
- function buildScene(stickmanArt, stickmanPos, showLogo, currentLogoArt, currentLogoPos,
142
  showLightningBolt, lightningTipX, lightningTipY, lightningLength,
143
  currentExplosionArt, explosionPos,
144
  showIngazeLogo, ingazeArt, ingazePos) {
145
  let sceneLines = Array(scene_height).fill(" ".repeat(scene_width));
146
 
147
- // 1. Data particles (drawn first, can be overwritten)
148
- data_particles.forEach(p => {
149
  const px = Math.round(p.x);
150
  const py = Math.round(p.y);
151
  if (py >= 0 && py < sceneLines.length && px >= 0 && px < scene_width) {
@@ -157,49 +138,32 @@
157
  }
158
  });
159
 
160
- // 2. Stickman
161
  overlay(sceneLines, stickmanArt, stickmanPos.x, stickmanPos.y);
162
-
163
- // 3. LinkedIn Logo
164
- if (showLogo) {
165
- overlay(sceneLines, currentLogoArt, currentLogoPos.x, currentLogoPos.y);
166
- }
167
-
168
- // 4. Lightning Bolt
169
  if (showLightningBolt && lightningTipY >=0) {
170
- drawLightningBolt(sceneLines, lightningTipX, lightningTipY, lightningLength);
171
  }
172
-
173
- // 5. Explosion
174
  if (currentExplosionArt && currentExplosionArt.length > 0) {
175
- overlay(sceneLines, currentExplosionArt, explosionPos.x, explosionPos.y, false); // false: allow spaces in explosion art
176
- }
177
-
178
- // 6. Ingaze Logo
179
- if (showIngazeLogo) {
180
- overlay(sceneLines, ingazeArt, ingazePos.x, ingazePos.y);
181
  }
 
182
 
183
  return sceneLines.join('\n');
184
  }
185
 
186
- function spawnDataParticle(logoX, logoY, logoWidth) {
187
- if (data_particles.length >= MAX_DATA_PARTICLES) return;
188
  const char = Math.random() > 0.5 ? '1' : '0';
189
  const x = logoX + 1 + Math.floor(Math.random() * (logoWidth - 2));
190
  const y = logoY + linked_in_art.length;
191
  const vy = 0.2 + Math.random() * 0.3;
192
- data_particles.push({ char, x, y, vy, active: true });
193
  }
194
 
195
- // --- Animation Sequence Generation ---
196
- // This function generates all frames once and stores them.
197
  function generateAnimationFrames() {
198
  const sequence = [];
199
- // Reset global state that might persist across generations if this is called multiple times
200
- data_particles = [];
201
- current_lightning_y = -1;
202
-
203
 
204
  const stickman_height = stick_man_art_normal.length;
205
  const stickman_width = stick_man_art_normal[0].length;
@@ -207,14 +171,11 @@
207
  const logo_height = linked_in_art.length;
208
 
209
  const stickman_base_y = FLOOR_LEVEL - stickman_height;
210
- const logo_base_y = stickman_base_y;
211
-
212
  const stickman_initial_pos = { x: 2, y: stickman_base_y };
213
- // Adjusted logo_initial_pos to be more to the right to give stickman space
214
- const logo_initial_pos = { x: scene_width - logo_width - 20, y: logo_base_y };
215
- const stickman_walk_target_x = logo_initial_pos.x - stickman_width - 1;
216
-
217
-
218
  const ingaze_pos = {
219
  x: Math.floor((scene_width - ingaze_logo_width) / 2),
220
  y: Math.floor((scene_height - ingaze_logo_height) / 2)
@@ -222,100 +183,66 @@
222
  const explosion_center_x = logo_initial_pos.x + Math.floor(logo_width / 2);
223
  const explosion_center_y = logo_initial_pos.y + Math.floor(logo_height / 2);
224
 
225
-
226
- // Phase 0: Stickman Walks to Logo
227
- // Ensure stickman_walk_target_x is not less than initial_pos.x if logo is too far left
228
- const target_x_for_walk = Math.max(stickman_initial_pos.x + 1, stickman_walk_target_x);
229
- const walk_frames = Math.max(10, Math.floor(Math.abs(target_x_for_walk - stickman_initial_pos.x) / 1.2) );
230
-
231
  let current_stickman_x = stickman_initial_pos.x;
232
- // Ensure walk_step_x is not NaN if walk_frames is 0 (though it's max(10, ...))
233
- const walk_step_x = walk_frames > 0 ? (target_x_for_walk - stickman_initial_pos.x) / walk_frames : 0;
234
 
235
  for (let i = 0; i < walk_frames; i++) {
236
  current_stickman_x += walk_step_x;
237
  const walk_art = (i % 4 < 2) ? stick_man_art_walk_1 : stick_man_art_walk_2;
238
- sequence.push(buildScene(walk_art, { x: Math.round(current_stickman_x), y: stickman_base_y }, true, linked_in_art, logo_initial_pos, false,0,0,0, [], {}, false, [], {}));
239
  }
240
- current_stickman_x = target_x_for_walk;
241
- let current_stickman_pos = { x: current_stickman_x, y: stickman_base_y };
242
 
243
- // Phase 1: Pause before shake
244
  for (let i = 0; i < 3; i++) {
245
- sequence.push(buildScene(stick_man_art_normal, current_stickman_pos, true, linked_in_art, logo_initial_pos, false,0,0,0, [], {}, false, [], {}));
246
  }
247
 
248
- // Phase 2: Shaking LinkedIn & Data Fall
249
  let current_logo_pos_phase2 = { ...logo_initial_pos };
250
- const shake_frames = 25;
251
  for (let i = 0; i < shake_frames; i++) {
252
  let logoAdj = { x: 0, y: 0 };
253
  if (i % 6 < 2) logoAdj = { x: 1, y: 0 }; else if (i % 6 < 4) logoAdj = { x: -1, y: 0 };
254
  current_logo_pos_phase2.x = logo_initial_pos.x + logoAdj.x;
255
- if (i % 2 === 0) spawnDataParticle(current_logo_pos_phase2.x, current_logo_pos_phase2.y, logo_width);
256
 
257
- // Create a new array for data_particles to update positions for this frame
258
- let next_frame_particles = [];
259
- data_particles.forEach(p => {
260
- let new_p = {...p};
261
- if (new_p.active) {
262
- new_p.y += new_p.vy;
263
- if (new_p.y >= FLOOR_LEVEL) {
264
- new_p.y = FLOOR_LEVEL;
265
- new_p.vy = 0;
266
- new_p.active = false;
267
- }
268
- }
269
- next_frame_particles.push(new_p);
270
  });
271
- data_particles = next_frame_particles; // Update global particles for next frame generation
272
-
273
- sequence.push(buildScene(stick_man_art_normal, current_stickman_pos, true, linked_in_art, current_logo_pos_phase2, false,0,0,0, [], {}, false, [], {}));
274
  }
275
- current_logo_pos_phase2 = { ...logo_initial_pos }; // Reset logo position
276
 
277
- // Phase 3: Data Settles, Stickman gets ready for impact
278
- const settle_frames = 10;
279
  for (let i = 0; i < settle_frames; i++) {
280
  let stickmanArtPhase3 = (i < settle_frames / 2) ? stick_man_art_normal : stick_man_art_surprise;
281
-
282
- let next_frame_particles_settle = [];
283
- data_particles.forEach(p => { // Ensure all particles settle
284
- let new_p = {...p};
285
- if (new_p.active) {
286
- new_p.y += new_p.vy;
287
- if (new_p.y >= FLOOR_LEVEL) {
288
- new_p.y = FLOOR_LEVEL;
289
- new_p.vy = 0;
290
- new_p.active = false;
291
- }
292
- }
293
- next_frame_particles_settle.push(new_p);
294
  });
295
- data_particles = next_frame_particles_settle;
296
-
297
- sequence.push(buildScene(stickmanArtPhase3, current_stickman_pos, true, linked_in_art, logo_initial_pos, false,0,0,0, [], {}, false, [], {}));
298
  }
299
 
300
  // Phase 4: Lightning Strike
301
  const lightning_target_y = logo_initial_pos.y;
302
- const lightning_strike_frames = lightning_target_y + 3;
303
- const lightning_bolt_length = 5;
304
  const lightning_tip_x_strike = logo_initial_pos.x + Math.floor(logo_width / 2);
305
- let frame_lightning_y = 0;
306
-
307
  for (let i = 0; i < lightning_strike_frames; i++) {
308
- frame_lightning_y = i;
309
  let logo_art_to_use = linked_in_art;
310
- if (frame_lightning_y >= lightning_target_y) {
311
  logo_art_to_use = linked_in_art_hit;
312
- frame_lightning_y = lightning_target_y;
313
  }
314
- // Update current_lightning_y for buildScene to use
315
- current_lightning_y = frame_lightning_y;
316
- sequence.push(buildScene(stick_man_art_surprise, current_stickman_pos, true, logo_art_to_use, logo_initial_pos, true, lightning_tip_x_strike, current_lightning_y, lightning_bolt_length, [], {}, false, [], {}));
317
  }
318
- current_lightning_y = -1; // Lightning done for buildScene logic
319
 
320
  // Phase 5: Explosion
321
  const explosion_duration_per_frame = 2;
@@ -328,99 +255,86 @@
328
  x: explosion_center_x - Math.floor(explosion_art_width / 2),
329
  y: explosion_center_y - Math.floor(explosion_art_height / 2)
330
  };
331
-
332
  for (let j = 0; j < explosion_duration_per_frame; j++) {
333
  if (i === 0 && j === 0) {
334
- data_particles = []; // Clear all data particles for buildScene
335
  stickman_art_explosion = stick_man_art_gone;
336
  }
337
- sequence.push(buildScene(stickman_art_explosion, current_stickman_pos, false, [], {}, false,0,0,0, explosion_art, current_explosion_pos, false, [], {}));
338
  }
339
  }
340
 
341
  // Phase 6: "Ingaze" Logo Reveal
342
- const ingaze_reveal_pause_frames = 5;
343
  for(let i=0; i < ingaze_reveal_pause_frames; i++) {
344
- sequence.push(buildScene(stick_man_art_gone, current_stickman_pos, false, [], {}, false,0,0,0, [], {}, false, [], {}));
345
  }
346
-
347
- const ingaze_display_frames = 30;
348
  for (let i = 0; i < ingaze_display_frames; i++) {
349
- sequence.push(buildScene(stick_man_art_gone, current_stickman_pos, false, [], {}, false,0,0,0, [], {}, true, ingaze_logo_art, ingaze_pos));
350
  }
351
  return sequence;
352
  }
353
 
354
-
355
  // --- Animation Player ---
356
  const animationElement = document.getElementById('cmd-animation');
357
- let currentFrame = 0;
358
- let animationInterval;
359
- let staticAnimationFrames = []; // Will hold the pre-generated frames
360
-
361
- function playAnimation() {
362
- // Ensure frames are generated before playing
363
- if (staticAnimationFrames.length === 0) {
364
- console.error("Animation frames not generated yet!");
365
- if(animationElement) animationElement.textContent = "Error: Animation frames missing.";
 
 
 
366
  return;
367
  }
368
 
369
- animationInterval = setInterval(() => {
370
- if (animationElement && staticAnimationFrames[currentFrame]) {
371
- animationElement.textContent = staticAnimationFrames[currentFrame];
372
  }
373
- currentFrame++;
374
- if (currentFrame >= staticAnimationFrames.length) {
375
- currentFrame = 0; // Loop animation
376
- // Re-initialize particle state for a clean loop if it's not handled by generation
377
- // The generateAnimationFrames function now resets particles at its start.
378
- // For a true visual loop, we might need to call generateAnimationFrames() again
379
- // or design the particle logic to reset cleanly on loop.
380
- // For now, a simple frame loop is implemented.
381
- // If generateAnimationFrames is heavy, avoid calling it every loop.
382
- // The current logic in generateAnimationFrames resets data_particles at the beginning.
383
- // To make the loop truly seamless with dynamic particles, the generation logic
384
- // would need to be part of the loop or the state reset more carefully.
385
- // Given the current structure, we'll loop the pre-generated sequence.
386
  }
387
  }, frameDuration);
388
  }
389
-
390
- // Initialize and start animation
391
- // Using a self-invoking function or ensuring DOM is ready
392
- (function() {
393
- // Check if the DOM is already loaded
394
- if (document.readyState === "complete" || document.readyState === "interactive") {
395
- initAnimation();
396
- } else {
397
- window.addEventListener('DOMContentLoaded', initAnimation);
398
- }
399
- })();
400
-
401
- function initAnimation() {
402
  if (!animationElement) {
403
- console.error("Animation element not found");
404
- return;
405
  }
406
  try {
407
- staticAnimationFrames = generateAnimationFrames(); // Generate all frames
408
- if (staticAnimationFrames.length > 0) {
409
- animationElement.textContent = staticAnimationFrames[0]; // Show first frame
410
- playAnimation();
411
- } else {
412
- animationElement.textContent = "Error: Could not generate animation.";
413
  }
414
  } catch (error) {
415
- console.error("Error during animation setup:", error);
416
- if(animationElement) animationElement.textContent = "Error initializing animation: " + error.message;
417
  }
418
  }
419
 
420
- // Clean up interval when the window/iframe is unloaded (optional, good practice)
 
 
 
 
 
 
 
 
421
  window.addEventListener('unload', () => {
422
- if (animationInterval) {
423
- clearInterval(animationInterval);
424
  }
425
  });
426
  </script>
 
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>CMD Ingaze Reveal Animation</title>
7
  <style>
8
+ /* Styles for the animation when embedded */
9
  body {
10
+ background-color: #000000; /* Black background for the animation area */
11
  color: #00FF00; /* Bright green text */
12
  font-family: 'Courier New', Courier, monospace; /* Classic CMD font */
13
+ margin: 0; /* Remove default margin */
14
+ padding: 0; /* Remove default padding */
15
+ overflow: hidden; /* Prevent scrollbars on the body of the iframe content */
16
+ display: flex; /* Flex container to center the animation content */
17
+ justify-content: center; /* Center horizontally */
18
+ align-items: center; /* Center vertically */
19
+ min-height: 350px; /* Minimum height for the animation area, adjust as needed */
20
  }
21
+
22
  #cmd-animation-container {
23
+ padding: 15px; /* Reduced padding slightly */
24
  border: 2px solid #00AA00; /* Darker green border */
25
  background-color: #080808; /* Slightly off-black for the "screen" */
26
+ box-shadow: 0 0 10px #00FF00; /* Green glow effect, slightly reduced */
27
  border-radius: 8px; /* Rounded corners for the container */
 
 
28
  box-sizing: border-box; /* Include padding and border in the element's total width and height */
29
+ /* width: 100%; */ /* Let it size based on pre content or set a max-width */
30
+ max-width: calc(100% - 10px); /* Ensure it fits if iframe has padding */
31
+ margin: 5px; /* Add a small margin around the container */
32
  }
33
+
34
  pre#cmd-animation {
35
  white-space: pre; /* Preserve whitespace and newlines */
36
  text-align: left;
37
+ font-size: 12px; /* Adjusted for potentially smaller iframe, ensure legibility */
38
+ line-height: 1.15; /* Compact lines, slightly increased for readability */
39
  margin: 0;
40
  overflow-x: auto; /* Add scroll if content is wider than container */
41
+ /* The height of the pre will be determined by its content (number of lines) */
42
  }
43
  </style>
44
  </head>
45
  <body>
46
  <div id="cmd-animation-container">
47
+ <pre id="cmd-animation">Loading Animation...</pre>
48
  </div>
49
 
50
  <script>
51
  // --- Configuration ---
52
+ const scene_width = 65; // Characters
53
  const scene_height = 22; // Lines
54
  const frameDuration = 150; // Milliseconds per frame
55
+ const MAX_DATA_PARTICLES = 100; // Reduced slightly
56
  const FLOOR_LEVEL = scene_height - 1;
57
 
58
  // --- ASCII Art Definitions ---
59
  const stick_man_art_normal = [" O ", " /|\\ ", " / \\ "];
60
  const stick_man_art_walk_1 = [" O ", " /|\\ ", " / > "];
61
  const stick_man_art_walk_2 = [" O ", " /|\\ ", " < \\ "];
62
+ const stick_man_art_surprise = [" \\O/ ", " | ", " / \\ "];
63
+ const stick_man_art_gone = [" ", " ", " "];
64
 
65
  const linked_in_art = ["+----+", "|i n |", "+----+"];
66
+ const linked_in_art_hit = ["x----x", "|BOOM|", "x----x"];
67
 
68
+ const lightning_strike_segment = "\\";
69
 
 
70
  const explosion_art_frames = [
71
+ [" * ", " *#* ", " * "],
72
+ [" @!% ", " @*#*& ", " X%O "],
73
+ [" $*~+@ ", "$#@!%*&", " X*O#~ ", " !+%$ "],
74
+ [" ~ ", " +*% ", " $ "]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  ];
76
 
77
  const ingaze_logo_art = [
 
84
  const ingaze_logo_width = ingaze_logo_art[0].length;
85
  const ingaze_logo_height = ingaze_logo_art.length;
86
 
87
+ // --- Global State (scoped within this script) ---
88
+ let data_particles_state = []; // Renamed to avoid conflict if script is ever not isolated
89
+ let current_lightning_y_state = -1;
 
90
 
91
  // --- Helper Functions ---
92
  function overlay(baseLines, artLines, startX, startY, ignoreSpaces = true) {
 
106
  });
107
  }
108
 
109
+ function drawLightningBoltInScene(sceneLines, tipX, tipY, length) {
110
  for (let i = 0; i < length; i++) {
111
  const y = tipY - i;
112
+ const x = tipX + (i % 2 === 0 ? 0 : (i % 4 === 1 ? -1 : 1));
113
  if (y >= 0 && y < scene_height && x >= 0 && x < scene_width) {
114
+ if (sceneLines[y][x] === ' ') {
115
  let lineArr = sceneLines[y].split('');
116
  lineArr[x] = lightning_strike_segment;
117
  sceneLines[y] = lineArr.join('');
 
120
  }
121
  }
122
 
123
+ function buildSceneFrame(stickmanArt, stickmanPos, showLogo, currentLogoArt, currentLogoPos,
 
124
  showLightningBolt, lightningTipX, lightningTipY, lightningLength,
125
  currentExplosionArt, explosionPos,
126
  showIngazeLogo, ingazeArt, ingazePos) {
127
  let sceneLines = Array(scene_height).fill(" ".repeat(scene_width));
128
 
129
+ data_particles_state.forEach(p => {
 
130
  const px = Math.round(p.x);
131
  const py = Math.round(p.y);
132
  if (py >= 0 && py < sceneLines.length && px >= 0 && px < scene_width) {
 
138
  }
139
  });
140
 
 
141
  overlay(sceneLines, stickmanArt, stickmanPos.x, stickmanPos.y);
142
+ if (showLogo) overlay(sceneLines, currentLogoArt, currentLogoPos.x, currentLogoPos.y);
 
 
 
 
 
 
143
  if (showLightningBolt && lightningTipY >=0) {
144
+ drawLightningBoltInScene(sceneLines, lightningTipX, lightningTipY, lightningLength);
145
  }
 
 
146
  if (currentExplosionArt && currentExplosionArt.length > 0) {
147
+ overlay(sceneLines, currentExplosionArt, explosionPos.x, explosionPos.y, false);
 
 
 
 
 
148
  }
149
+ if (showIngazeLogo) overlay(sceneLines, ingazeArt, ingazePos.x, ingazePos.y);
150
 
151
  return sceneLines.join('\n');
152
  }
153
 
154
+ function spawnNewDataParticle(logoX, logoY, logoWidth) {
155
+ if (data_particles_state.length >= MAX_DATA_PARTICLES) return;
156
  const char = Math.random() > 0.5 ? '1' : '0';
157
  const x = logoX + 1 + Math.floor(Math.random() * (logoWidth - 2));
158
  const y = logoY + linked_in_art.length;
159
  const vy = 0.2 + Math.random() * 0.3;
160
+ data_particles_state.push({ char, x, y, vy, active: true });
161
  }
162
 
 
 
163
  function generateAnimationFrames() {
164
  const sequence = [];
165
+ data_particles_state = []; // Reset particles for this generation
166
+ current_lightning_y_state = -1; // Reset lightning state
 
 
167
 
168
  const stickman_height = stick_man_art_normal.length;
169
  const stickman_width = stick_man_art_normal[0].length;
 
171
  const logo_height = linked_in_art.length;
172
 
173
  const stickman_base_y = FLOOR_LEVEL - stickman_height;
174
+ const logo_base_y = stickman_base_y;
 
175
  const stickman_initial_pos = { x: 2, y: stickman_base_y };
176
+ const logo_initial_pos = { x: scene_width - logo_width - 15, y: logo_base_y }; // Adjusted position
177
+ const stickman_walk_target_x = Math.max(stickman_initial_pos.x + 1, logo_initial_pos.x - stickman_width - 1);
178
+
 
 
179
  const ingaze_pos = {
180
  x: Math.floor((scene_width - ingaze_logo_width) / 2),
181
  y: Math.floor((scene_height - ingaze_logo_height) / 2)
 
183
  const explosion_center_x = logo_initial_pos.x + Math.floor(logo_width / 2);
184
  const explosion_center_y = logo_initial_pos.y + Math.floor(logo_height / 2);
185
 
186
+ // Phase 0: Stickman Walks
187
+ const walk_frames = Math.max(10, Math.floor(Math.abs(stickman_walk_target_x - stickman_initial_pos.x) / 1.0) );
 
 
 
 
188
  let current_stickman_x = stickman_initial_pos.x;
189
+ const walk_step_x = walk_frames > 0 ? (stickman_walk_target_x - stickman_initial_pos.x) / walk_frames : 0;
 
190
 
191
  for (let i = 0; i < walk_frames; i++) {
192
  current_stickman_x += walk_step_x;
193
  const walk_art = (i % 4 < 2) ? stick_man_art_walk_1 : stick_man_art_walk_2;
194
+ sequence.push(buildSceneFrame(walk_art, { x: Math.round(current_stickman_x), y: stickman_base_y }, true, linked_in_art, logo_initial_pos, false,0,0,0, [], {}, false, [], {}));
195
  }
196
+ current_stickman_x = stickman_walk_target_x;
197
+ let current_stickman_pos = { x: Math.round(current_stickman_x), y: stickman_base_y };
198
 
199
+ // Phase 1: Pause
200
  for (let i = 0; i < 3; i++) {
201
+ sequence.push(buildSceneFrame(stick_man_art_normal, current_stickman_pos, true, linked_in_art, logo_initial_pos, false,0,0,0, [], {}, false, [], {}));
202
  }
203
 
204
+ // Phase 2: Shaking & Data Fall
205
  let current_logo_pos_phase2 = { ...logo_initial_pos };
206
+ const shake_frames = 20; // Reduced shake frames
207
  for (let i = 0; i < shake_frames; i++) {
208
  let logoAdj = { x: 0, y: 0 };
209
  if (i % 6 < 2) logoAdj = { x: 1, y: 0 }; else if (i % 6 < 4) logoAdj = { x: -1, y: 0 };
210
  current_logo_pos_phase2.x = logo_initial_pos.x + logoAdj.x;
211
+ if (i % 3 === 0) spawnNewDataParticle(current_logo_pos_phase2.x, current_logo_pos_phase2.y, logo_width);
212
 
213
+ data_particles_state.forEach(p => {
214
+ if (p.active) { p.y += p.vy; if (p.y >= FLOOR_LEVEL) { p.y = FLOOR_LEVEL; p.vy = 0; p.active = false; } }
 
 
 
 
 
 
 
 
 
 
 
215
  });
216
+ sequence.push(buildSceneFrame(stick_man_art_normal, current_stickman_pos, true, linked_in_art, current_logo_pos_phase2, false,0,0,0, [], {}, false, [], {}));
 
 
217
  }
218
+ current_logo_pos_phase2 = { ...logo_initial_pos };
219
 
220
+ // Phase 3: Data Settles, Stickman Surprised
221
+ const settle_frames = 8;
222
  for (let i = 0; i < settle_frames; i++) {
223
  let stickmanArtPhase3 = (i < settle_frames / 2) ? stick_man_art_normal : stick_man_art_surprise;
224
+ data_particles_state.forEach(p => {
225
+ if (p.active) { p.y += p.vy; if (p.y >= FLOOR_LEVEL) { p.y = FLOOR_LEVEL; p.vy = 0; p.active = false; } }
 
 
 
 
 
 
 
 
 
 
 
226
  });
227
+ sequence.push(buildSceneFrame(stickmanArtPhase3, current_stickman_pos, true, linked_in_art, logo_initial_pos, false,0,0,0, [], {}, false, [], {}));
 
 
228
  }
229
 
230
  // Phase 4: Lightning Strike
231
  const lightning_target_y = logo_initial_pos.y;
232
+ const lightning_strike_frames = lightning_target_y + 2;
233
+ const lightning_bolt_length = 4;
234
  const lightning_tip_x_strike = logo_initial_pos.x + Math.floor(logo_width / 2);
235
+
 
236
  for (let i = 0; i < lightning_strike_frames; i++) {
237
+ current_lightning_y_state = i; // This is the tip's current y as it descends
238
  let logo_art_to_use = linked_in_art;
239
+ if (current_lightning_y_state >= lightning_target_y) {
240
  logo_art_to_use = linked_in_art_hit;
241
+ current_lightning_y_state = lightning_target_y; // Hold at target
242
  }
243
+ sequence.push(buildSceneFrame(stick_man_art_surprise, current_stickman_pos, true, logo_art_to_use, logo_initial_pos, true, lightning_tip_x_strike, current_lightning_y_state, lightning_bolt_length, [], {}, false, [], {}));
 
 
244
  }
245
+ current_lightning_y_state = -1; // Reset lightning state
246
 
247
  // Phase 5: Explosion
248
  const explosion_duration_per_frame = 2;
 
255
  x: explosion_center_x - Math.floor(explosion_art_width / 2),
256
  y: explosion_center_y - Math.floor(explosion_art_height / 2)
257
  };
 
258
  for (let j = 0; j < explosion_duration_per_frame; j++) {
259
  if (i === 0 && j === 0) {
260
+ data_particles_state = [];
261
  stickman_art_explosion = stick_man_art_gone;
262
  }
263
+ sequence.push(buildSceneFrame(stickman_art_explosion, current_stickman_pos, false, [], {}, false,0,0,0, explosion_art, current_explosion_pos, false, [], {}));
264
  }
265
  }
266
 
267
  // Phase 6: "Ingaze" Logo Reveal
268
+ const ingaze_reveal_pause_frames = 4;
269
  for(let i=0; i < ingaze_reveal_pause_frames; i++) {
270
+ sequence.push(buildSceneFrame(stick_man_art_gone, current_stickman_pos, false, [], {}, false,0,0,0, [], {}, false, [], {}));
271
  }
272
+ const ingaze_display_frames = 25;
 
273
  for (let i = 0; i < ingaze_display_frames; i++) {
274
+ sequence.push(buildSceneFrame(stick_man_art_gone, current_stickman_pos, false, [], {}, false,0,0,0, [], {}, true, ingaze_logo_art, ingaze_pos));
275
  }
276
  return sequence;
277
  }
278
 
 
279
  // --- Animation Player ---
280
  const animationElement = document.getElementById('cmd-animation');
281
+ let currentFrameIndex = 0;
282
+ let animationIntervalId;
283
+ let allAnimationFrames = [];
284
+
285
+ function runAnimation() {
286
+ if (!animationElement) {
287
+ console.error("Animation element 'cmd-animation' not found.");
288
+ return;
289
+ }
290
+ if (allAnimationFrames.length === 0) {
291
+ console.error("Animation frames are empty. Cannot play animation.");
292
+ animationElement.textContent = "Error: No frames.";
293
  return;
294
  }
295
 
296
+ animationIntervalId = setInterval(() => {
297
+ if (allAnimationFrames[currentFrameIndex]) {
298
+ animationElement.textContent = allAnimationFrames[currentFrameIndex];
299
  }
300
+ currentFrameIndex++;
301
+ if (currentFrameIndex >= allAnimationFrames.length) {
302
+ currentFrameIndex = 0; // Loop animation
 
 
 
 
 
 
 
 
 
 
303
  }
304
  }, frameDuration);
305
  }
306
+
307
+ function initializeAnimation() {
 
 
 
 
 
 
 
 
 
 
 
308
  if (!animationElement) {
309
+ // console.warn("Animation element not ready yet in initializeAnimation.");
310
+ return; // Element not ready
311
  }
312
  try {
313
+ allAnimationFrames = generateAnimationFrames();
314
+ if (allAnimationFrames.length > 0 && animationElement) {
315
+ animationElement.textContent = allAnimationFrames[0];
316
+ runAnimation();
317
+ } else if (animationElement) {
318
+ animationElement.textContent = "Error: Could not generate animation frames.";
319
  }
320
  } catch (error) {
321
+ console.error("Error during animation generation or initial play:", error);
322
+ if(animationElement) animationElement.textContent = "Error initializing: " + error.message;
323
  }
324
  }
325
 
326
+ // Start animation once DOM is ready
327
+ if (document.readyState === "complete" || document.readyState === "interactive") {
328
+ // DOM already loaded
329
+ setTimeout(initializeAnimation, 0); // Use setTimeout to ensure it runs after current execution block
330
+ } else {
331
+ document.addEventListener('DOMContentLoaded', initializeAnimation);
332
+ }
333
+
334
+ // Clean up interval when the window/iframe is unloaded
335
  window.addEventListener('unload', () => {
336
+ if (animationIntervalId) {
337
+ clearInterval(animationIntervalId);
338
  }
339
  });
340
  </script>