Update camera configuration and enhance touch controls; implement debounce for camera state logging and add responsive styles for mobile devices.
This commit is contained in:
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"camera": {
|
||||
"position": {
|
||||
"x": 450,
|
||||
"y": 154,
|
||||
"z": -130
|
||||
"x": 514,
|
||||
"y": 228,
|
||||
"z": -403
|
||||
},
|
||||
"target": {
|
||||
"x": 50,
|
||||
"y": 85,
|
||||
"z": -140
|
||||
},
|
||||
"distance": 406,
|
||||
"distance": 552,
|
||||
"spherical": {
|
||||
"radius": 406,
|
||||
"theta": 1.55,
|
||||
"phi": 1.4
|
||||
"radius": 552,
|
||||
"theta": 2.09,
|
||||
"phi": 1.31
|
||||
}
|
||||
},
|
||||
"description": "Default camera configuration for 3D model viewer. Position values can be copied from console logs and pasted here to set new defaults."
|
||||
|
||||
153
src/index.js
153
src/index.js
@@ -120,88 +120,6 @@ async function init() {
|
||||
|
||||
function setupCustomTouchControls() {
|
||||
const canvas = renderer.domElement;
|
||||
let touchStartDistance = 0;
|
||||
let lastTouchMoveTime = 0;
|
||||
|
||||
// Variables for touch tracking
|
||||
let touches = [];
|
||||
let isZooming = false;
|
||||
|
||||
// Helper function to get touch distance (for pinch zoom)
|
||||
function getTouchDistance(touch1, touch2) {
|
||||
const dx = touch1.clientX - touch2.clientX;
|
||||
const dy = touch1.clientY - touch2.clientY;
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
// Touch start handler - only handle two-finger gestures, let OrbitControls handle single finger
|
||||
canvas.addEventListener('touchstart', function(event) {
|
||||
touches = Array.from(event.touches);
|
||||
|
||||
if (touches.length === 2) {
|
||||
// Two finger pinch/zoom start - prevent default for custom handling
|
||||
event.preventDefault();
|
||||
touchStartDistance = getTouchDistance(touches[0], touches[1]);
|
||||
isZooming = true;
|
||||
} else {
|
||||
// Single finger - let OrbitControls handle it natively
|
||||
isZooming = false;
|
||||
}
|
||||
|
||||
lastTouchMoveTime = Date.now();
|
||||
}, { passive: false });
|
||||
|
||||
// Touch move handler - only custom handle pinch zoom
|
||||
canvas.addEventListener('touchmove', function(event) {
|
||||
const currentTime = Date.now();
|
||||
const timeDelta = currentTime - lastTouchMoveTime;
|
||||
|
||||
// Throttle touch move events for performance (max 60fps)
|
||||
if (timeDelta < 16) return;
|
||||
|
||||
touches = Array.from(event.touches);
|
||||
|
||||
if (touches.length === 2 && isZooming) {
|
||||
// Prevent default only for two-finger gestures
|
||||
event.preventDefault();
|
||||
|
||||
// Handle pinch zoom
|
||||
const currentDistance = getTouchDistance(touches[0], touches[1]);
|
||||
const zoomDelta = (currentDistance - touchStartDistance) * 0.01;
|
||||
|
||||
// Apply zoom with smooth scaling
|
||||
const zoomFactor = Math.exp(-zoomDelta);
|
||||
const newDistance = controls.object.position.distanceTo(controls.target) * zoomFactor;
|
||||
|
||||
// Clamp zoom within limits
|
||||
const clampedDistance = Math.max(controls.minDistance,
|
||||
Math.min(controls.maxDistance, newDistance));
|
||||
|
||||
// Update camera position
|
||||
const direction = controls.object.position.clone().sub(controls.target).normalize();
|
||||
controls.object.position.copy(controls.target).add(direction.multiplyScalar(clampedDistance));
|
||||
|
||||
touchStartDistance = currentDistance;
|
||||
controls.update();
|
||||
}
|
||||
// Single finger rotation handled natively by OrbitControls
|
||||
|
||||
lastTouchMoveTime = currentTime;
|
||||
}, { passive: false });
|
||||
|
||||
// Touch end handler
|
||||
canvas.addEventListener('touchend', function(event) {
|
||||
touches = Array.from(event.touches);
|
||||
|
||||
if (touches.length < 2) {
|
||||
isZooming = false;
|
||||
}
|
||||
}, { passive: false });
|
||||
|
||||
// Prevent context menu on long press
|
||||
canvas.addEventListener('contextmenu', function(event) {
|
||||
event.preventDefault();
|
||||
}, { passive: false });
|
||||
|
||||
// Add touch-friendly CSS for better interaction
|
||||
canvas.style.touchAction = 'none';
|
||||
@@ -210,7 +128,12 @@ function setupCustomTouchControls() {
|
||||
canvas.style.mozUserSelect = 'none';
|
||||
canvas.style.msUserSelect = 'none';
|
||||
|
||||
console.log('Custom touch controls initialized - OrbitControls handles single finger rotation');
|
||||
// Prevent context menu on long press
|
||||
canvas.addEventListener('contextmenu', function(event) {
|
||||
event.preventDefault();
|
||||
}, { passive: false });
|
||||
|
||||
console.log('Touch controls enabled - OrbitControls handles all touch gestures natively');
|
||||
}
|
||||
|
||||
function setupLighting() {
|
||||
@@ -544,37 +467,49 @@ function centerModelAndControls(object) {
|
||||
}
|
||||
|
||||
function setupRotationLogging() {
|
||||
// Log rotation and zoom changes whenever the camera moves
|
||||
let debounceTimer = null;
|
||||
|
||||
// Log rotation and zoom changes with 1-second debounce
|
||||
controls.addEventListener('change', function() {
|
||||
const distance = camera.position.distanceTo(controls.target);
|
||||
const position = camera.position.clone();
|
||||
const target = controls.target.clone();
|
||||
// Clear existing timer
|
||||
if (debounceTimer) {
|
||||
clearTimeout(debounceTimer);
|
||||
}
|
||||
|
||||
// Calculate spherical coordinates for rotation
|
||||
const spherical = new THREE.Spherical().setFromVector3(
|
||||
position.clone().sub(target)
|
||||
);
|
||||
// Set new timer for 1 second delay
|
||||
debounceTimer = setTimeout(() => {
|
||||
const distance = camera.position.distanceTo(controls.target);
|
||||
const position = camera.position.clone();
|
||||
const target = controls.target.clone();
|
||||
|
||||
console.log('=== CAMERA STATE ===');
|
||||
console.log('Position:', {
|
||||
x: Math.round(position.x * 100) / 100,
|
||||
y: Math.round(position.y * 100) / 100,
|
||||
z: Math.round(position.z * 100) / 100
|
||||
});
|
||||
console.log('Target:', {
|
||||
x: Math.round(target.x * 100) / 100,
|
||||
y: Math.round(target.y * 100) / 100,
|
||||
z: Math.round(target.z * 100) / 100
|
||||
});
|
||||
console.log('Distance (Zoom):', Math.round(distance * 100) / 100);
|
||||
console.log('Spherical Rotation:');
|
||||
console.log(' - Radius:', Math.round(spherical.radius * 100) / 100);
|
||||
console.log(' - Theta (horizontal):', Math.round(spherical.theta * 100) / 100, 'radians');
|
||||
console.log(' - Phi (vertical):', Math.round(spherical.phi * 100) / 100, 'radians');
|
||||
console.log('==================');
|
||||
// Calculate spherical coordinates for rotation
|
||||
const spherical = new THREE.Spherical().setFromVector3(
|
||||
position.clone().sub(target)
|
||||
);
|
||||
|
||||
console.log('=== CAMERA STATE ===');
|
||||
console.log('Position:', {
|
||||
x: Math.round(position.x * 100) / 100,
|
||||
y: Math.round(position.y * 100) / 100,
|
||||
z: Math.round(position.z * 100) / 100
|
||||
});
|
||||
console.log('Target:', {
|
||||
x: Math.round(target.x * 100) / 100,
|
||||
y: Math.round(target.y * 100) / 100,
|
||||
z: Math.round(target.z * 100) / 100
|
||||
});
|
||||
console.log('Distance (Zoom):', Math.round(distance * 100) / 100);
|
||||
console.log('Spherical Rotation:');
|
||||
console.log(' - Radius:', Math.round(spherical.radius * 100) / 100);
|
||||
console.log(' - Theta (horizontal):', Math.round(spherical.theta * 100) / 100, 'radians');
|
||||
console.log(' - Phi (vertical):', Math.round(spherical.phi * 100) / 100, 'radians');
|
||||
console.log('==================');
|
||||
|
||||
debounceTimer = null;
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
console.log('Rotation logging enabled - camera state will be logged on every change');
|
||||
console.log('Rotation logging enabled - camera state will be logged 1 second after movement stops');
|
||||
}
|
||||
|
||||
function animate() {
|
||||
|
||||
@@ -23,6 +23,46 @@ body {
|
||||
border-radius: 10px;
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
backdrop-filter: blur(10px);
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
/* Mobile and tablet responsive styles - covers both portrait and landscape */
|
||||
@media (max-width: 1024px) {
|
||||
#info {
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
padding: 4px 6px;
|
||||
font-size: 10px;
|
||||
max-width: 140px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#info h2 {
|
||||
font-size: 11px;
|
||||
margin: 0 0 3px 0;
|
||||
}
|
||||
|
||||
#info p {
|
||||
margin: 1px 0;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
#info hr {
|
||||
margin: 3px 0 !important;
|
||||
border: 0.5px solid #555;
|
||||
}
|
||||
}
|
||||
|
||||
/* Very small screens (phones in portrait) */
|
||||
@media (max-width: 480px) {
|
||||
#info {
|
||||
max-width: 120px;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
#info h2 {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
#loading {
|
||||
|
||||
Reference in New Issue
Block a user