183 lines
5.1 KiB
TypeScript
183 lines
5.1 KiB
TypeScript
import { useRef, useEffect } from 'react';
|
|
import { Canvas, useFrame, useThree } from '@react-three/fiber';
|
|
import { OrbitControls, Stars, Float } from '@react-three/drei';
|
|
import * as THREE from 'three';
|
|
import Characters from './Characters';
|
|
|
|
// Warm gradient background
|
|
function WarmBackground() {
|
|
const { scene } = useThree();
|
|
|
|
useEffect(() => {
|
|
// Create gradient texture
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = 512;
|
|
canvas.height = 512;
|
|
const ctx = canvas.getContext('2d')!;
|
|
|
|
const gradient = ctx.createLinearGradient(0, 0, 0, 512);
|
|
gradient.addColorStop(0, '#FFE5D4');
|
|
gradient.addColorStop(0.5, '#FFB4A2');
|
|
gradient.addColorStop(1, '#FF8E72');
|
|
|
|
ctx.fillStyle = gradient;
|
|
ctx.fillRect(0, 0, 512, 512);
|
|
|
|
const texture = new THREE.TextureLoader().load(canvas.toDataURL());
|
|
scene.background = texture;
|
|
}, [scene]);
|
|
|
|
return null;
|
|
}
|
|
|
|
// Ground plane with warm color
|
|
function Ground() {
|
|
const meshRef = useRef<THREE.Mesh>(null);
|
|
|
|
useFrame((state) => {
|
|
if (meshRef.current) {
|
|
// Subtle ground animation
|
|
meshRef.current.rotation.x = -Math.PI / 2;
|
|
}
|
|
});
|
|
|
|
return (
|
|
<mesh ref={meshRef} position={[0, -0.1, 0]} receiveShadow>
|
|
<planeGeometry args={[20, 20, 32, 32]} />
|
|
<meshStandardMaterial
|
|
color="#F5FAFF"
|
|
roughness={0.8}
|
|
metalness={0.1}
|
|
/>
|
|
</mesh>
|
|
);
|
|
}
|
|
|
|
// Floating particles for atmosphere
|
|
function FloatingParticles({ isCelebrating }: { isCelebrating: boolean }) {
|
|
const points = useRef<THREE.Points>(null);
|
|
const particleCount = isCelebrating ? 200 : 50;
|
|
|
|
const positions = new Float32Array(particleCount * 3);
|
|
for (let i = 0; i < particleCount; i++) {
|
|
positions[i * 3] = (Math.random() - 0.5) * 15;
|
|
positions[i * 3 + 1] = Math.random() * 8;
|
|
positions[i * 3 + 2] = (Math.random() - 0.5) * 15;
|
|
}
|
|
|
|
useFrame((state) => {
|
|
if (points.current) {
|
|
points.current.rotation.y = state.clock.getElapsedTime() * 0.05;
|
|
if (isCelebrating) {
|
|
points.current.rotation.y = state.clock.getElapsedTime() * 0.2;
|
|
}
|
|
}
|
|
});
|
|
|
|
return (
|
|
<points ref={points}>
|
|
<bufferGeometry>
|
|
<bufferAttribute
|
|
attach="attributes-position"
|
|
args={[positions, 3]}
|
|
/>
|
|
</bufferGeometry>
|
|
<pointsMaterial
|
|
size={0.1}
|
|
color={isCelebrating ? "#53B1FD" : "#84CAFF"}
|
|
transparent
|
|
opacity={0.8}
|
|
sizeAttenuation
|
|
/>
|
|
</points>
|
|
);
|
|
}
|
|
|
|
// Blue lighting setup
|
|
function Lighting() {
|
|
return (
|
|
<>
|
|
<ambientLight intensity={0.6} color="#D1E9FF" />
|
|
<directionalLight
|
|
position={[5, 10, 5]}
|
|
intensity={1.2}
|
|
color="#53B1FD"
|
|
castShadow
|
|
shadow-mapSize={[2048, 2048]}
|
|
/>
|
|
<pointLight position={[-5, 5, -5]} intensity={0.8} color="#84CAFF" />
|
|
<pointLight position={[5, 3, 5]} intensity={0.6} color="#2E90FA" />
|
|
</>
|
|
);
|
|
}
|
|
|
|
// Camera animation
|
|
function CameraController({ isCelebrating }: { isCelebrating: boolean }) {
|
|
const { camera } = useThree();
|
|
|
|
useFrame((state) => {
|
|
const time = state.clock.getElapsedTime();
|
|
|
|
if (isCelebrating) {
|
|
// Dramatic camera movement during celebration
|
|
camera.position.x = Math.sin(time * 0.5) * 2;
|
|
camera.position.y = 3 + Math.sin(time * 0.3) * 0.5;
|
|
camera.position.z = 8 + Math.cos(time * 0.5) * 1;
|
|
} else {
|
|
// Gentle orbiting before celebration
|
|
camera.position.x = Math.sin(time * 0.2) * 4;
|
|
camera.position.z = 8 + Math.cos(time * 0.2) * 2;
|
|
camera.position.y = 3;
|
|
}
|
|
|
|
camera.lookAt(0, 1, 0);
|
|
});
|
|
|
|
return null;
|
|
}
|
|
|
|
interface SceneProps {
|
|
isCelebrating: boolean;
|
|
}
|
|
|
|
export default function Scene({ isCelebrating }: SceneProps) {
|
|
return (
|
|
<div className="w-full h-screen">
|
|
<Canvas
|
|
shadows
|
|
camera={{ position: [0, 3, 8], fov: 60 }}
|
|
gl={{ antialias: true, alpha: true }}
|
|
>
|
|
<WarmBackground />
|
|
<Lighting />
|
|
<CameraController isCelebrating={isCelebrating} />
|
|
|
|
<Ground />
|
|
<Characters isCelebrating={isCelebrating} />
|
|
<FloatingParticles isCelebrating={isCelebrating} />
|
|
|
|
{/* Floating decorative elements */}
|
|
<Float speed={2} rotationIntensity={0.5} floatIntensity={0.5}>
|
|
<mesh position={[-3, 4, -2]}>
|
|
<sphereGeometry args={[0.3, 16, 16]} />
|
|
<meshStandardMaterial color="#84CAFF" emissive="#53B1FD" emissiveIntensity={0.3} />
|
|
</mesh>
|
|
</Float>
|
|
|
|
<Float speed={1.5} rotationIntensity={0.3} floatIntensity={0.3}>
|
|
<mesh position={[3, 5, -3]}>
|
|
<sphereGeometry args={[0.2, 16, 16]} />
|
|
<meshStandardMaterial color="#53B1FD" emissive="#2E90FA" emissiveIntensity={0.3} />
|
|
</mesh>
|
|
</Float>
|
|
|
|
<Float speed={2.5} rotationIntensity={0.4} floatIntensity={0.4}>
|
|
<mesh position={[2, 3, 2]}>
|
|
<sphereGeometry args={[0.25, 16, 16]} />
|
|
<meshStandardMaterial color="#2E90FA" emissive="#1570EF" emissiveIntensity={0.2} />
|
|
</mesh>
|
|
</Float>
|
|
</Canvas>
|
|
</div>
|
|
);
|
|
}
|