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>
);
};
|