refactor feature folders by code type

This commit is contained in:
Tom Boullay
2026-04-28 14:14:15 +02:00
parent eebeee9ed8
commit 2251a81ac1
22 changed files with 24 additions and 24 deletions
+126
View File
@@ -0,0 +1,126 @@
import {
useRef,
useEffect,
useCallback,
forwardRef,
useImperativeHandle,
type ElementRef,
} from "react";
import { useFrame, useThree } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import * as THREE from "three";
type OrbitControlsRef = ElementRef<typeof OrbitControls>;
interface FlyControllerProps {
speed?: number;
verticalSpeed?: number;
onPositionChange?: (position: THREE.Vector3) => void;
disabled?: boolean;
}
interface FlyControllerRef {
controls: OrbitControlsRef | null;
}
export const FlyController = forwardRef<FlyControllerRef, FlyControllerProps>(
(
{ speed = 10, verticalSpeed = 5, onPositionChange, disabled = false },
ref,
) => {
const { camera: rawCamera } = useThree();
const cameraRef = useRef(rawCamera);
const keys = useRef<{ [key: string]: boolean }>({});
const controlsRef = useRef<OrbitControlsRef | null>(null);
const lastPosition = useRef(new THREE.Vector3());
useImperativeHandle(ref, () => ({
controls: controlsRef.current,
}));
const handleKeyDown = useCallback((e: KeyboardEvent) => {
keys.current[e.code] = true;
}, []);
const handleKeyUp = useCallback((e: KeyboardEvent) => {
keys.current[e.code] = false;
}, []);
useEffect(() => {
window.addEventListener("keydown", handleKeyDown);
window.addEventListener("keyup", handleKeyUp);
return () => {
window.removeEventListener("keydown", handleKeyDown);
window.removeEventListener("keyup", handleKeyUp);
};
}, [handleKeyDown, handleKeyUp]);
useFrame((_, delta) => {
// Disabled mode keeps OrbitControls active without keyboard movement.
if (disabled) {
return;
}
// Supports AZERTY, QWERTY, and arrow-key movement.
const isForward =
keys.current["KeyW"] || keys.current["KeyZ"] || keys.current["ArrowUp"];
const isBackward = keys.current["KeyS"] || keys.current["ArrowDown"];
const isLeft =
keys.current["KeyQ"] ||
keys.current["KeyA"] ||
keys.current["ArrowLeft"];
const isRight = keys.current["KeyD"] || keys.current["ArrowRight"];
const direction = new THREE.Vector3();
const frontVector = new THREE.Vector3(
0,
0,
Number(isBackward) - Number(isForward),
);
const sideVector = new THREE.Vector3(
Number(isRight) - Number(isLeft),
0,
0,
);
direction.subVectors(frontVector, sideVector);
if (direction.lengthSq() > 0) {
direction.normalize().multiplyScalar(speed * delta);
direction.applyQuaternion(cameraRef.current.quaternion);
cameraRef.current.position.add(direction);
}
// Space moves up; Shift moves down.
if (keys.current["Space"]) {
cameraRef.current.position.y += verticalSpeed * delta;
}
if (keys.current["ShiftLeft"] || keys.current["ShiftRight"]) {
cameraRef.current.position.y -= verticalSpeed * delta;
}
if (
onPositionChange &&
!cameraRef.current.position.equals(lastPosition.current)
) {
lastPosition.current.copy(cameraRef.current.position);
onPositionChange(cameraRef.current.position);
}
});
return (
<OrbitControls
ref={controlsRef}
makeDefault
enableDamping
dampingFactor={0.05}
mouseButtons={{
LEFT: THREE.MOUSE.ROTATE,
MIDDLE: THREE.MOUSE.DOLLY,
RIGHT: THREE.MOUSE.PAN,
}}
/>
);
},
);
FlyController.displayName = "FlyController";