SRP
This commit is contained in:
111
src/composables/three/useCameraControls.ts
Normal file
111
src/composables/three/useCameraControls.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import * as THREE from 'three';
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
||||
import { useSimulation } from '../useSimulation';
|
||||
|
||||
export function useCameraControls(camera: THREE.Camera, renderer: THREE.WebGLRenderer) {
|
||||
let controls: OrbitControls;
|
||||
|
||||
// Camera Motion State
|
||||
let moveAnimating = false;
|
||||
let moveStart: THREE.Vector3 | null = null;
|
||||
let moveEnd: THREE.Vector3 | null = null;
|
||||
let contStart: THREE.Vector3 | null = null;
|
||||
let contEnd: THREE.Vector3 | null = null;
|
||||
let moveProgress = 0;
|
||||
const moveDuration = 0.2;
|
||||
let moveStartTime = 0;
|
||||
|
||||
const { state } = useSimulation(); // Access global state
|
||||
|
||||
const initControls = () => {
|
||||
controls = new OrbitControls(camera, renderer.domElement);
|
||||
controls.enableDamping = false;
|
||||
controls.dampingFactor = 0.05;
|
||||
controls.enableRotate = false;
|
||||
controls.enablePan = true;
|
||||
controls.enableZoom = false;
|
||||
controls.screenSpacePanning = false; // Fix: Pan on XZ plane only
|
||||
// Strictly match legacy controls.js
|
||||
controls.mouseButtons = {
|
||||
LEFT: THREE.MOUSE.PAN
|
||||
} as any; // Cast to allow partial object if TS complains, or just because we are overriding defaults
|
||||
controls.target.set(0, 0, 0);
|
||||
|
||||
// Scroll Listeners
|
||||
controls.addEventListener('change', () => {
|
||||
if (moveAnimating) return;
|
||||
controls.target.y = 0;
|
||||
camera.position.y = Math.min(camera.position.y, state.value.maxZoom || 120);
|
||||
// This 'change' listener might fight with the zoom logic if not careful.
|
||||
// But let's just stick to the requested change: Zoom Out limit.
|
||||
});
|
||||
|
||||
renderer.domElement.addEventListener('wheel', (event: WheelEvent) => {
|
||||
event.preventDefault();
|
||||
if (!moveAnimating) {
|
||||
const zoomAmount = state.value.zoomSensitivity || 5;
|
||||
const maxZoom = state.value.maxZoom || 120; // Default fallback
|
||||
|
||||
// Calculate direction from target to camera (view vector)
|
||||
const dir = new THREE.Vector3().subVectors(camera.position, controls.target).normalize();
|
||||
let targetPos: THREE.Vector3 | null = null;
|
||||
|
||||
if (event.deltaY < 0) {
|
||||
// Zoom Out: Move away from target
|
||||
targetPos = camera.position.clone().add(dir.multiplyScalar(zoomAmount));
|
||||
// Check Max Limit
|
||||
if (targetPos.y > maxZoom) {
|
||||
// Clamp to max zoom height, but keep angle (roughly, or just stop)
|
||||
// Simple stop:
|
||||
targetPos = null;
|
||||
|
||||
// Or precise clamping (complex math to find point on vector where y=maxZoom):
|
||||
// y = y0 + t * dir.y => maxZoom = y0 + t * dir.y => t = (maxZoom - y0) / dir.y
|
||||
// But simply ignoring the input if it exceeds is safer/smoother for now.
|
||||
}
|
||||
} else if (event.deltaY > 0) {
|
||||
// Zoom In: Move towards target
|
||||
targetPos = camera.position.clone().sub(dir.multiplyScalar(zoomAmount));
|
||||
// Check Min Limit
|
||||
if (targetPos.y < 10) {
|
||||
targetPos = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetPos) {
|
||||
moveStart = camera.position.clone();
|
||||
moveEnd = targetPos;
|
||||
contStart = controls.target.clone();
|
||||
contEnd = controls.target.clone(); // Target stays same
|
||||
moveProgress = 0;
|
||||
moveStartTime = performance.now() / 1000;
|
||||
moveAnimating = true;
|
||||
}
|
||||
}
|
||||
}, { passive: false });
|
||||
};
|
||||
|
||||
const updateControls = () => {
|
||||
// Animation
|
||||
if (moveAnimating && moveStart && moveEnd && contStart && contEnd) {
|
||||
const now = performance.now() / 1000;
|
||||
moveProgress = Math.min((now - moveStartTime) / moveDuration, 1);
|
||||
|
||||
camera.position.lerpVectors(moveStart, moveEnd, moveProgress);
|
||||
controls.target.lerpVectors(contStart, contEnd, moveProgress);
|
||||
|
||||
if (moveProgress >= 1) {
|
||||
moveAnimating = false;
|
||||
}
|
||||
controls.update();
|
||||
}
|
||||
|
||||
if (controls) controls.update();
|
||||
};
|
||||
|
||||
return {
|
||||
initControls,
|
||||
updateControls,
|
||||
getControls: () => controls
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user