mirror of
https://github.com/CNCKitchen/stlTexturizer.git
synced 2026-04-07 22:11:32 +00:00
feat: enhance pinch-to-zoom with two-finger pan support for touch devices
This commit is contained in:
+32
-16
@@ -269,14 +269,16 @@ export function initViewer(canvas) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pinch-to-zoom for touch devices
|
// Pinch-to-zoom + two-finger pan for touch devices
|
||||||
let _pinchDist = null;
|
let _pinchDist = null;
|
||||||
|
let _pinchMid = null; // { x, y } client coords of two-finger midpoint
|
||||||
|
|
||||||
renderer.domElement.addEventListener('touchstart', (e) => {
|
renderer.domElement.addEventListener('touchstart', (e) => {
|
||||||
if (e.touches.length === 2) {
|
if (e.touches.length === 2) {
|
||||||
const t0 = e.touches[0], t1 = e.touches[1];
|
const t0 = e.touches[0], t1 = e.touches[1];
|
||||||
_pinchDist = Math.hypot(t1.clientX - t0.clientX, t1.clientY - t0.clientY);
|
_pinchDist = Math.hypot(t1.clientX - t0.clientX, t1.clientY - t0.clientY);
|
||||||
controls.enabled = false; // suppress OrbitControls panning during pinch
|
_pinchMid = { x: (t0.clientX + t1.clientX) / 2, y: (t0.clientY + t1.clientY) / 2 };
|
||||||
|
controls.enabled = false; // suppress OrbitControls during two-finger gesture
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}, { passive: false });
|
}, { passive: false });
|
||||||
@@ -285,31 +287,45 @@ export function initViewer(canvas) {
|
|||||||
if (e.touches.length !== 2 || _pinchDist === null) return;
|
if (e.touches.length !== 2 || _pinchDist === null) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const t0 = e.touches[0], t1 = e.touches[1];
|
const t0 = e.touches[0], t1 = e.touches[1];
|
||||||
const newDist = Math.hypot(t1.clientX - t0.clientX, t1.clientY - t0.clientY);
|
|
||||||
const factor = newDist / _pinchDist;
|
|
||||||
_pinchDist = newDist;
|
|
||||||
|
|
||||||
// Midpoint in NDC — zoom toward the centre of the two fingers
|
|
||||||
const rect = renderer.domElement.getBoundingClientRect();
|
const rect = renderer.domElement.getBoundingClientRect();
|
||||||
const midX = (t0.clientX + t1.clientX) / 2;
|
|
||||||
const midY = (t0.clientY + t1.clientY) / 2;
|
|
||||||
const ndcX = ((midX - rect.left) / rect.width) * 2 - 1;
|
|
||||||
const ndcY = -((midY - rect.top) / rect.height) * 2 + 1;
|
|
||||||
|
|
||||||
const before = new THREE.Vector3(ndcX, ndcY, 0).unproject(camera);
|
const newDist = Math.hypot(t1.clientX - t0.clientX, t1.clientY - t0.clientY);
|
||||||
|
const midX = (t0.clientX + t1.clientX) / 2;
|
||||||
|
const midY = (t0.clientY + t1.clientY) / 2;
|
||||||
|
|
||||||
|
// ── Pan: shift camera so the world point under the old midpoint
|
||||||
|
// is now under the new midpoint ──────────────────────────
|
||||||
|
const prevNdcX = ((_pinchMid.x - rect.left) / rect.width) * 2 - 1;
|
||||||
|
const prevNdcY = -((_pinchMid.y - rect.top) / rect.height) * 2 + 1;
|
||||||
|
const curNdcX = ((midX - rect.left) / rect.width) * 2 - 1;
|
||||||
|
const curNdcY = -((midY - rect.top) / rect.height) * 2 + 1;
|
||||||
|
|
||||||
|
const prevWorld = new THREE.Vector3(prevNdcX, prevNdcY, 0).unproject(camera);
|
||||||
|
const curWorld = new THREE.Vector3(curNdcX, curNdcY, 0).unproject(camera);
|
||||||
|
const panDelta = prevWorld.sub(curWorld);
|
||||||
|
camera.position.add(panDelta);
|
||||||
|
controls.target.add(panDelta);
|
||||||
|
|
||||||
|
// ── Zoom: zoom toward the current midpoint ────────────────────────
|
||||||
|
const factor = newDist / _pinchDist;
|
||||||
|
const before = new THREE.Vector3(curNdcX, curNdcY, 0).unproject(camera);
|
||||||
camera.zoom = Math.max(0.05, Math.min(200, camera.zoom * factor));
|
camera.zoom = Math.max(0.05, Math.min(200, camera.zoom * factor));
|
||||||
camera.updateProjectionMatrix();
|
camera.updateProjectionMatrix();
|
||||||
const after = new THREE.Vector3(ndcX, ndcY, 0).unproject(camera);
|
const after = new THREE.Vector3(curNdcX, curNdcY, 0).unproject(camera);
|
||||||
|
|
||||||
const delta = before.clone().sub(after);
|
const zoomDelta = before.clone().sub(after);
|
||||||
camera.position.add(delta);
|
camera.position.add(zoomDelta);
|
||||||
controls.target.add(delta);
|
controls.target.add(zoomDelta);
|
||||||
|
|
||||||
|
_pinchDist = newDist;
|
||||||
|
_pinchMid = { x: midX, y: midY };
|
||||||
controls.update();
|
controls.update();
|
||||||
}, { passive: false });
|
}, { passive: false });
|
||||||
|
|
||||||
renderer.domElement.addEventListener('touchend', (e) => {
|
renderer.domElement.addEventListener('touchend', (e) => {
|
||||||
if (e.touches.length < 2) {
|
if (e.touches.length < 2) {
|
||||||
_pinchDist = null;
|
_pinchDist = null;
|
||||||
|
_pinchMid = null;
|
||||||
controls.enabled = true;
|
controls.enabled = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user