implemented touch controls for the camera
This commit is contained in:
parent
e8c31b23ee
commit
a6b31c30c9
1 changed files with 90 additions and 0 deletions
|
|
@ -73,6 +73,7 @@ export default function OrbitCameraControls(props: Props) {
|
|||
const isPanning = useRef(false);
|
||||
const lastPos = useRef({ x: 0, y: 0 });
|
||||
const panCameraPosition = useRef(new Vector3());
|
||||
const lastPinchDist = useRef(0);
|
||||
|
||||
// Spherical coords
|
||||
const spherical = useRef({
|
||||
|
|
@ -222,16 +223,105 @@ export default function OrbitCameraControls(props: Props) {
|
|||
updateCamera();
|
||||
};
|
||||
|
||||
const handleTouchStart = (e: TouchEvent) => {
|
||||
if (e.target !== canvas) return;
|
||||
e.preventDefault();
|
||||
|
||||
if (e.touches.length === 1) {
|
||||
isDragging.current = true;
|
||||
isPanning.current = false;
|
||||
lastPos.current = { x: e.touches[0].clientX, y: e.touches[0].clientY };
|
||||
} else if (e.touches.length === 2) {
|
||||
isDragging.current = false;
|
||||
isPanning.current = true;
|
||||
lastPos.current = {
|
||||
x: (e.touches[0].clientX + e.touches[1].clientX) / 2,
|
||||
y: (e.touches[0].clientY + e.touches[1].clientY) / 2,
|
||||
};
|
||||
// Store initial pinch distance for zoom
|
||||
const dx = e.touches[0].clientX - e.touches[1].clientX;
|
||||
const dy = e.touches[0].clientY - e.touches[1].clientY;
|
||||
lastPinchDist.current = Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTouchMove = (e: TouchEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (e.touches.length === 1 && isDragging.current) {
|
||||
const deltaX = e.touches[0].clientX - lastPos.current.x;
|
||||
const deltaY = e.touches[0].clientY - lastPos.current.y;
|
||||
spherical.current.theta -= deltaX * 0.005;
|
||||
spherical.current.phi -= deltaY * 0.005;
|
||||
if (clampVerticalRotation) {
|
||||
spherical.current.phi = Math.max(minPhi, Math.min(maxPhi, spherical.current.phi));
|
||||
}
|
||||
lastPos.current = { x: e.touches[0].clientX, y: e.touches[0].clientY };
|
||||
updateCamera();
|
||||
|
||||
} else if (e.touches.length === 2) {
|
||||
// Pinch zoom
|
||||
const dx = e.touches[0].clientX - e.touches[1].clientX;
|
||||
const dy = e.touches[0].clientY - e.touches[1].clientY;
|
||||
const dist = Math.sqrt(dx * dx + dy * dy);
|
||||
const pinchDelta = lastPinchDist.current - dist;
|
||||
spherical.current.radius += pinchDelta * 0.05;
|
||||
spherical.current.radius = Math.max(3, Math.min(maxRadius ?? 30, spherical.current.radius));
|
||||
lastPinchDist.current = dist;
|
||||
|
||||
// Two-finger pan
|
||||
const midX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
|
||||
const midY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
|
||||
const panDeltaX = midX - lastPos.current.x;
|
||||
const panDeltaY = midY - lastPos.current.y;
|
||||
|
||||
const distance = spherical.current.radius;
|
||||
const panSpeed = 0.002 * distance;
|
||||
const forward = new Vector3();
|
||||
camera.getWorldDirection(forward);
|
||||
const right = new Vector3().crossVectors(forward, new Vector3(0, 1, 0)).normalize();
|
||||
if (right.lengthSq() < 0.001) right.crossVectors(forward, camera.up).normalize();
|
||||
const screenUp = new Vector3().crossVectors(right, forward).normalize();
|
||||
right.multiplyScalar(-panDeltaX * panSpeed);
|
||||
screenUp.multiplyScalar(panDeltaY * panSpeed);
|
||||
const pan = new Vector3().addVectors(right, screenUp);
|
||||
targetRef.current.x += pan.x;
|
||||
targetRef.current.y += pan.y;
|
||||
targetRef.current.z += pan.z;
|
||||
|
||||
lastPos.current = { x: midX, y: midY };
|
||||
updateCamera();
|
||||
}
|
||||
};
|
||||
|
||||
const handleTouchEnd = (e: TouchEvent) => {
|
||||
if (e.touches.length === 0) {
|
||||
isDragging.current = false;
|
||||
isPanning.current = false;
|
||||
} else if (e.touches.length === 1) {
|
||||
// Transitioned from pinch back to single finger
|
||||
isDragging.current = true;
|
||||
isPanning.current = false;
|
||||
lastPos.current = { x: e.touches[0].clientX, y: e.touches[0].clientY };
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("mousedown", handleMouseDown);
|
||||
window.addEventListener("mousemove", handleMouseMove);
|
||||
window.addEventListener("mouseup", handleMouseUp);
|
||||
canvas.addEventListener("wheel", handleWheel, { passive: false });
|
||||
canvas.addEventListener("touchstart", handleTouchStart, { passive: false });
|
||||
canvas.addEventListener("touchmove", handleTouchMove, { passive: false });
|
||||
canvas.addEventListener("touchend", handleTouchEnd);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("mousedown", handleMouseDown);
|
||||
window.removeEventListener("mousemove", handleMouseMove);
|
||||
window.removeEventListener("mouseup", handleMouseUp);
|
||||
canvas.removeEventListener("wheel", handleWheel);
|
||||
canvas.removeEventListener("touchstart", handleTouchStart);
|
||||
canvas.removeEventListener("touchmove", handleTouchMove);
|
||||
canvas.removeEventListener("touchend", handleTouchEnd);
|
||||
};
|
||||
}, [camera, gl, target, clampVerticalRotation, minPhi, maxPhi, initialRadius, maxRadius]);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue