Trae Assistant commited on
Commit
aa989e4
·
1 Parent(s): c868c0f
Files changed (2) hide show
  1. static/js/main.js +63 -46
  2. templates/index.html +1 -1
static/js/main.js CHANGED
@@ -157,8 +157,8 @@ createApp({
157
  this.x = window.innerWidth / 2;
158
  this.y = window.innerHeight + 200;
159
  this.angle = -Math.PI / 2;
160
- this.scale = 1.5; // Smaller, more refined
161
- this.state = 'IDLE'; // IDLE, FOLLOW, CHARGING, SLASHING
162
 
163
  // Spirit Particles
164
  this.spirits = [];
@@ -183,7 +183,7 @@ createApp({
183
  }
184
 
185
  // Position
186
- const ease = 0.15;
187
  this.x += dx * ease;
188
  this.y += dy * ease;
189
 
@@ -343,8 +343,11 @@ createApp({
343
  loading.value = false;
344
  if (!canvasElement.value) return;
345
 
346
- canvasElement.value.width = window.innerWidth;
347
- canvasElement.value.height = window.innerHeight;
 
 
 
348
 
349
  // Screen Shake
350
  let shakeX = 0;
@@ -374,11 +377,14 @@ createApp({
374
  canvasCtx.globalAlpha = 1.0;
375
 
376
  // Hand & Gesture Logic
377
- let targetX = sword ? sword.x : window.innerWidth / 2;
378
- let targetY = sword ? sword.y : window.innerHeight + 300;
 
 
 
379
  let detectedGesture = 'NONE';
380
 
381
- if (results.multiHandLandmarks && results.multiHandLandmarks.length > 0) {
382
  const lm = results.multiHandLandmarks[0];
383
  const isFingerExtended = (tipIdx, pipIdx) => lm[tipIdx].y < lm[pipIdx].y;
384
 
@@ -411,56 +417,65 @@ createApp({
411
  lastGesture = detectedGesture;
412
  }
413
 
414
- // Init Sword
415
- if (!sword) {
416
  sword = new Sword();
417
- for(let i=0; i<80; i++) {
418
- stars.push({
419
- x: Math.random() * window.innerWidth,
420
- y: Math.random() * window.innerHeight,
421
- size: Math.random() * 2,
422
- opacity: Math.random() * 0.5
423
- });
 
 
 
 
 
 
 
424
  }
425
  }
426
 
427
  // State Machine
428
  const STABLE = 5;
429
 
430
- if (gestureTimer > STABLE) {
431
- if (detectedGesture === 'SWORD_FINGER') {
432
- // Charging State
433
- sword.state = 'CHARGING';
434
- if (chargeLevel < 1.0) {
435
- chargeLevel += 0.02;
436
- if (Math.random() < 0.2) playSound('CHARGE');
437
- }
438
- } else if (detectedGesture === 'OPEN_PALM') {
439
- // If Charged, Trigger SPLIT
440
- if (chargeLevel > 0.8) {
441
- if (!riftEffect) {
442
- riftEffect = new RiftEffect();
443
- shockwave = 1.0;
444
- playSound('SLASH');
445
- // Sparks
446
- for(let i=0; i<100; i++) {
447
- particles.push(new Particle(targetX, targetY, 'SPARK'));
 
 
 
448
  }
449
- chargeLevel = 0; // Reset
 
 
 
450
  }
451
  } else {
452
- // Just Follow
453
- sword.state = 'FOLLOW';
454
  chargeLevel = Math.max(0, chargeLevel - 0.05);
455
  }
456
- } else {
457
- sword.state = 'IDLE';
458
- chargeLevel = Math.max(0, chargeLevel - 0.05);
459
  }
460
- }
461
 
462
- // Update Sword
463
- sword.update(targetX, targetY);
 
464
 
465
  // Draw Rift (Behind Sword or Front? Front is better for "Split Sky")
466
  if (riftEffect) {
@@ -471,13 +486,15 @@ createApp({
471
 
472
  // Particles
473
  particles.forEach((p, i) => {
474
- p.update(sword.x, sword.y);
475
  p.draw(canvasCtx);
476
  if (p.life <= 0 && p.type !== 'SPIRIT') particles.splice(i, 1);
477
  });
478
 
479
  // Draw Sword
480
- sword.draw(canvasCtx);
 
 
481
 
482
  canvasCtx.restore();
483
  };
 
157
  this.x = window.innerWidth / 2;
158
  this.y = window.innerHeight + 200;
159
  this.angle = -Math.PI / 2;
160
+ this.scale = 1.5;
161
+ this.state = 'IDLE';
162
 
163
  // Spirit Particles
164
  this.spirits = [];
 
183
  }
184
 
185
  // Position
186
+ const ease = 0.2; // Slightly faster for better responsiveness
187
  this.x += dx * ease;
188
  this.y += dy * ease;
189
 
 
343
  loading.value = false;
344
  if (!canvasElement.value) return;
345
 
346
+ // Optimize: Only resize if dimensions changed
347
+ if (canvasElement.value.width !== window.innerWidth || canvasElement.value.height !== window.innerHeight) {
348
+ canvasElement.value.width = window.innerWidth;
349
+ canvasElement.value.height = window.innerHeight;
350
+ }
351
 
352
  // Screen Shake
353
  let shakeX = 0;
 
377
  canvasCtx.globalAlpha = 1.0;
378
 
379
  // Hand & Gesture Logic
380
+ const hasHand = results.multiHandLandmarks && results.multiHandLandmarks.length > 0;
381
+
382
+ // Default target (when no hand): Center of screen
383
+ let targetX = window.innerWidth / 2;
384
+ let targetY = window.innerHeight / 2;
385
  let detectedGesture = 'NONE';
386
 
387
+ if (hasHand) {
388
  const lm = results.multiHandLandmarks[0];
389
  const isFingerExtended = (tipIdx, pipIdx) => lm[tipIdx].y < lm[pipIdx].y;
390
 
 
417
  lastGesture = detectedGesture;
418
  }
419
 
420
+ // Init Sword Logic: Only create if hand is detected
421
+ if (hasHand && !sword) {
422
  sword = new Sword();
423
+ // Spawn at the bottom (off-screen) to animate "Sword Coming"
424
+ sword.x = window.innerWidth / 2;
425
+ sword.y = window.innerHeight + 200;
426
+
427
+ // Initialize stars if not already done
428
+ if (stars.length === 0) {
429
+ for(let i=0; i<80; i++) {
430
+ stars.push({
431
+ x: Math.random() * window.innerWidth,
432
+ y: Math.random() * window.innerHeight,
433
+ size: Math.random() * 2,
434
+ opacity: Math.random() * 0.5
435
+ });
436
+ }
437
  }
438
  }
439
 
440
  // State Machine
441
  const STABLE = 5;
442
 
443
+ if (sword) {
444
+ if (gestureTimer > STABLE) {
445
+ if (detectedGesture === 'SWORD_FINGER') {
446
+ // Charging State
447
+ sword.state = 'CHARGING';
448
+ if (chargeLevel < 1.0) {
449
+ chargeLevel += 0.02;
450
+ if (Math.random() < 0.2) playSound('CHARGE');
451
+ }
452
+ } else if (detectedGesture === 'OPEN_PALM') {
453
+ // If Charged, Trigger SPLIT
454
+ if (chargeLevel > 0.8) {
455
+ if (!riftEffect) {
456
+ riftEffect = new RiftEffect();
457
+ shockwave = 1.0;
458
+ playSound('SLASH');
459
+ // Sparks
460
+ for(let i=0; i<100; i++) {
461
+ particles.push(new Particle(targetX, targetY, 'SPARK'));
462
+ }
463
+ chargeLevel = 0; // Reset
464
  }
465
+ } else {
466
+ // Just Follow
467
+ sword.state = 'FOLLOW';
468
+ chargeLevel = Math.max(0, chargeLevel - 0.05);
469
  }
470
  } else {
471
+ sword.state = 'IDLE';
 
472
  chargeLevel = Math.max(0, chargeLevel - 0.05);
473
  }
 
 
 
474
  }
 
475
 
476
+ // Update Sword
477
+ sword.update(targetX, targetY);
478
+ }
479
 
480
  // Draw Rift (Behind Sword or Front? Front is better for "Split Sky")
481
  if (riftEffect) {
 
486
 
487
  // Particles
488
  particles.forEach((p, i) => {
489
+ p.update(sword ? sword.x : 0, sword ? sword.y : 0);
490
  p.draw(canvasCtx);
491
  if (p.life <= 0 && p.type !== 'SPIRIT') particles.splice(i, 1);
492
  });
493
 
494
  // Draw Sword
495
+ if (sword) {
496
+ sword.draw(canvasCtx);
497
+ }
498
 
499
  canvasCtx.restore();
500
  };
templates/index.html CHANGED
@@ -37,7 +37,7 @@
37
 
38
  <!-- UI Layer -->
39
  <div class="ui-layer flex flex-col justify-between p-4">
40
- <div class="text-center mt-4">
41
  <h1 class="text-3xl md:text-5xl font-bold text-white tracking-widest title-text opacity-80">剑 来 · 开 天</h1>
42
  <p class="text-gray-300 text-xs md:text-sm mt-2 opacity-60">剑指聚气,挥掌开天</p>
43
  </div>
 
37
 
38
  <!-- UI Layer -->
39
  <div class="ui-layer flex flex-col justify-between p-4">
40
+ <div class="text-center mt-10">
41
  <h1 class="text-3xl md:text-5xl font-bold text-white tracking-widest title-text opacity-80">剑 来 · 开 天</h1>
42
  <p class="text-gray-300 text-xs md:text-sm mt-2 opacity-60">剑指聚气,挥掌开天</p>
43
  </div>