All files / components/endscreen/components VictoryAnimation3D.tsx

3.57% Statements 1/28
0% Branches 0/6
0% Functions 0/3
3.7% Lines 1/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141                  1x                                                                                                                                                                                                                                                                      
import { useFrame } from "@react-three/fiber";
import React, { useMemo, useRef } from "react";
import * as THREE from "three";
import { KOREAN_COLORS } from "../../../types/constants";
 
/**
 * Victory Animation 3D Component
 * Displays celebratory 3D particle effects for victory screen
 */
export const VictoryAnimation3D: React.FC = () => {
  const groupRef = useRef<THREE.Group>(null);
  const particlesRef = useRef<THREE.Points>(null);
  const ringsRef = useRef<THREE.Group>(null);
 
  // Create victory particles
  const particlePositions = useMemo(() => {
    const count = 150;
    const positions = new Float32Array(count * 3);
 
    for (let i = 0; i < count; i++) {
      const i3 = i * 3;
      const radius = 3 + Math.random() * 2;
      const theta = Math.random() * Math.PI * 2;
      const phi = Math.random() * Math.PI;
 
      positions[i3] = radius * Math.sin(phi) * Math.cos(theta);
      positions[i3 + 1] = radius * Math.cos(phi);
      positions[i3 + 2] = radius * Math.sin(phi) * Math.sin(theta);
    }
 
    return positions;
  }, []);
 
  // Animate victory effects
  useFrame((state) => {
    const time = state.clock.elapsedTime;
 
    // Rotate entire group
    if (groupRef.current) {
      groupRef.current.rotation.y = time * 0.3;
    }
 
    // Pulse particles
    if (particlesRef.current) {
      const scale = 1 + Math.sin(time * 2) * 0.2;
      particlesRef.current.scale.setScalar(scale);
    }
 
    // Rotate rings
    if (ringsRef.current) {
      ringsRef.current.rotation.x = time * 0.5;
      ringsRef.current.rotation.z = time * 0.3;
    }
  });
 
  return (
    <group ref={groupRef} position={[0, 2, 0]} data-testid="victory-animation-3d">
      {/* Victory particles */}
      <points ref={particlesRef}>
        <bufferGeometry>
          <bufferAttribute
            attach="attributes-position"
            count={150}
            itemSize={3}
            args={[particlePositions, 3]}
          />
        </bufferGeometry>
        <pointsMaterial
          size={0.2}
          color={new THREE.Color(KOREAN_COLORS.ACCENT_GOLD)}
          transparent
          opacity={0.8}
          sizeAttenuation
          depthWrite={false}
        />
      </points>
 
      {/* Rotating rings */}
      <group ref={ringsRef}>
        <mesh rotation={[Math.PI / 2, 0, 0]}>
          <torusGeometry args={[2, 0.05, 16, 100]} />
          <meshBasicMaterial
            color={KOREAN_COLORS.PRIMARY_CYAN}
            transparent
            opacity={0.6}
          />
        </mesh>
 
        <mesh rotation={[Math.PI / 2, Math.PI / 4, 0]}>
          <torusGeometry args={[2.5, 0.05, 16, 100]} />
          <meshBasicMaterial
            color={KOREAN_COLORS.ACCENT_GOLD}
            transparent
            opacity={0.4}
          />
        </mesh>
 
        <mesh rotation={[Math.PI / 2, Math.PI / 2, 0]}>
          <torusGeometry args={[3, 0.05, 16, 100]} />
          <meshBasicMaterial
            color={KOREAN_COLORS.PRIMARY_CYAN}
            transparent
            opacity={0.3}
          />
        </mesh>
      </group>
 
      {/* Central glow sphere */}
      <mesh>
        <sphereGeometry args={[0.5, 32, 32]} />
        <meshStandardMaterial
          color={KOREAN_COLORS.ACCENT_GOLD}
          emissive={KOREAN_COLORS.ACCENT_GOLD}
          emissiveIntensity={1.5}
          transparent
          opacity={0.8}
        />
      </mesh>
 
      {/* Outer glow */}
      <mesh>
        <sphereGeometry args={[0.8, 32, 32]} />
        <meshBasicMaterial
          color={KOREAN_COLORS.PRIMARY_CYAN}
          transparent
          opacity={0.2}
          side={THREE.BackSide}
        />
      </mesh>
 
      {/* Point light for glow effect */}
      <pointLight
        position={[0, 0, 0]}
        intensity={3}
        distance={10}
        color={KOREAN_COLORS.ACCENT_GOLD}
      />
    </group>
  );
};