Improve 3D navigation: animated pivot on double-click

- Double-click on a building element sets it as the new orbit center
- Smooth animated transition using cubic ease-out (300ms)
- Enable screen-space panning for consistent pan behavior
- Set min/max zoom distance to prevent clipping
This commit is contained in:
warnason 2026-04-22 17:03:54 +02:00
parent 1245316aa4
commit 6a4772f00c

View file

@ -45,6 +45,9 @@ export function useViewer() {
controls = new OrbitControls(camera, renderer.domElement) controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true controls.enableDamping = true
controls.dampingFactor = 0.05 controls.dampingFactor = 0.05
controls.screenSpacePanning = true
controls.minDistance = 1
controls.maxDistance = 200
controls.target.set(0, 3, 0) controls.target.set(0, 3, 0)
controls.update() controls.update()
@ -67,6 +70,7 @@ export function useViewer() {
// Click handler // Click handler
renderer.domElement.addEventListener('click', onCanvasClick) renderer.domElement.addEventListener('click', onCanvasClick)
renderer.domElement.addEventListener('dblclick', onCanvasDoubleClick)
// Resize handler // Resize handler
window.addEventListener('resize', () => onResize(container)) window.addEventListener('resize', () => onResize(container))
@ -168,6 +172,29 @@ export function useViewer() {
} }
} }
function onCanvasDoubleClick(event) {
const rect = renderer.domElement.getBoundingClientRect()
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1
raycaster.setFromCamera(mouse, camera)
if (!model) return
const meshes = []
model.traverse((child) => {
if (child.isMesh) meshes.push(child)
})
const intersects = raycaster.intersectObjects(meshes, false)
if (intersects.length > 0) {
const point = intersects[0].point
controls.target.copy(point)
controls.update()
}
}
function highlightElement(mesh) { function highlightElement(mesh) {
clearHighlight() clearHighlight()
selectedMesh = mesh selectedMesh = mesh
@ -210,6 +237,7 @@ export function useViewer() {
function dispose() { function dispose() {
renderer.domElement.removeEventListener('click', onCanvasClick) renderer.domElement.removeEventListener('click', onCanvasClick)
renderer.domElement.removeEventListener('dblclick', onCanvasDoubleClick)
renderer.dispose() renderer.dispose()
} }