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 isPanning = useRef(false);
|
||||||
const lastPos = useRef({ x: 0, y: 0 });
|
const lastPos = useRef({ x: 0, y: 0 });
|
||||||
const panCameraPosition = useRef(new Vector3());
|
const panCameraPosition = useRef(new Vector3());
|
||||||
|
const lastPinchDist = useRef(0);
|
||||||
|
|
||||||
// Spherical coords
|
// Spherical coords
|
||||||
const spherical = useRef({
|
const spherical = useRef({
|
||||||
|
|
@ -222,16 +223,105 @@ export default function OrbitCameraControls(props: Props) {
|
||||||
updateCamera();
|
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("mousedown", handleMouseDown);
|
||||||
window.addEventListener("mousemove", handleMouseMove);
|
window.addEventListener("mousemove", handleMouseMove);
|
||||||
window.addEventListener("mouseup", handleMouseUp);
|
window.addEventListener("mouseup", handleMouseUp);
|
||||||
canvas.addEventListener("wheel", handleWheel, { passive: false });
|
canvas.addEventListener("wheel", handleWheel, { passive: false });
|
||||||
|
canvas.addEventListener("touchstart", handleTouchStart, { passive: false });
|
||||||
|
canvas.addEventListener("touchmove", handleTouchMove, { passive: false });
|
||||||
|
canvas.addEventListener("touchend", handleTouchEnd);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("mousedown", handleMouseDown);
|
window.removeEventListener("mousedown", handleMouseDown);
|
||||||
window.removeEventListener("mousemove", handleMouseMove);
|
window.removeEventListener("mousemove", handleMouseMove);
|
||||||
window.removeEventListener("mouseup", handleMouseUp);
|
window.removeEventListener("mouseup", handleMouseUp);
|
||||||
canvas.removeEventListener("wheel", handleWheel);
|
canvas.removeEventListener("wheel", handleWheel);
|
||||||
|
canvas.removeEventListener("touchstart", handleTouchStart);
|
||||||
|
canvas.removeEventListener("touchmove", handleTouchMove);
|
||||||
|
canvas.removeEventListener("touchend", handleTouchEnd);
|
||||||
};
|
};
|
||||||
}, [camera, gl, target, clampVerticalRotation, minPhi, maxPhi, initialRadius, maxRadius]);
|
}, [camera, gl, target, clampVerticalRotation, minPhi, maxPhi, initialRadius, maxRadius]);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue