96 lines
2.4 KiB
JavaScript
96 lines
2.4 KiB
JavaScript
|
|
import { useEffect, useMemo, useState } from 'react'
|
|
import { Canvas, useFrame, useThree } from '@react-three/fiber'
|
|
import * as THREE from 'three'
|
|
|
|
const PRESETS = {
|
|
front: [0, 1.2, 6.5],
|
|
side: [6.5, 1.4, 0],
|
|
topAngled: [3.8, 5.4, 4.2],
|
|
}
|
|
|
|
function CameraController({ preset }) {
|
|
const { camera } = useThree()
|
|
const target = useMemo(() => new THREE.Vector3(0, 0, 0), [])
|
|
const targetPosition = useMemo(() => new THREE.Vector3(), [])
|
|
|
|
useEffect(() => {
|
|
const [x, y, z] = PRESETS[preset]
|
|
targetPosition.set(x, y, z)
|
|
}, [preset, targetPosition])
|
|
|
|
useFrame(() => {
|
|
camera.position.lerp(targetPosition, 0.08)
|
|
camera.lookAt(target)
|
|
})
|
|
|
|
return null
|
|
}
|
|
|
|
function Scene({ preset }) {
|
|
return (
|
|
<>
|
|
<ambientLight intensity={0.85} />
|
|
<directionalLight intensity={1} position={[3, 4, 5]} />
|
|
|
|
<mesh>
|
|
<boxGeometry args={[1.6, 1.6, 1.6]} />
|
|
<meshStandardMaterial color="#2a9d8f" roughness={0.45} metalness={0.1} />
|
|
</mesh>
|
|
|
|
<CameraController preset={preset} />
|
|
</>
|
|
)
|
|
}
|
|
|
|
function App() {
|
|
const [preset, setPreset] = useState('front')
|
|
|
|
useEffect(() => {
|
|
function onKeyDown(event) {
|
|
if (event.key === '1') setPreset('front')
|
|
if (event.key === '2') setPreset('side')
|
|
if (event.key === '3') setPreset('topAngled')
|
|
}
|
|
|
|
window.addEventListener('keydown', onKeyDown)
|
|
return () => window.removeEventListener('keydown', onKeyDown)
|
|
}, [])
|
|
|
|
return (
|
|
<div className="app-shell">
|
|
<div id="preset-panel">
|
|
<h2>Camera Presets</h2>
|
|
<div className="preset-buttons">
|
|
<button
|
|
className={`preset-btn ${preset === 'front' ? 'active' : ''}`}
|
|
onClick={() => setPreset('front')}
|
|
>
|
|
Front (1)
|
|
</button>
|
|
<button
|
|
className={`preset-btn ${preset === 'side' ? 'active' : ''}`}
|
|
onClick={() => setPreset('side')}
|
|
>
|
|
Side (2)
|
|
</button>
|
|
<button
|
|
className={`preset-btn ${preset === 'topAngled' ? 'active' : ''}`}
|
|
onClick={() => setPreset('topAngled')}
|
|
>
|
|
Top Angled (3)
|
|
</button>
|
|
</div>
|
|
<p id="hint">Press 1, 2, or 3 to switch view.</p>
|
|
</div>
|
|
|
|
<Canvas camera={{ fov: 55, near: 0.1, far: 100, position: PRESETS.front }}>
|
|
<color attach="background" args={["#f3f5f8"]} />
|
|
<Scene preset={preset} />
|
|
</Canvas>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default App
|