diff --git a/ui/static/graph.html b/ui/static/graph.html index 568e324..ef698f2 100644 --- a/ui/static/graph.html +++ b/ui/static/graph.html @@ -947,6 +947,10 @@ function init() { } else { selectedNode = node; showNodeInfo(node.userData.nodeData, true); + // 自动视角:点击节点时飞向它 + if (autoViewEnabled) { + flyToNode(node.userData.nodeId, 500); + } } } } @@ -1210,6 +1214,31 @@ function smoothReposition() { } }); }); + + // 自动视角:高亮节点被读取时,飞向它们的中心 + if (autoViewEnabled && currentHighlightIds.length > 0 && !data.new_node_id) { + const meshes = nodeMeshes.filter(m => currentHighlightIds.includes(m.userData.nodeId)); + if (meshes.length > 0) { + const avgPos = new THREE.Vector3(0, 0, 0); + meshes.forEach(m => avgPos.add(m.position)); + avgPos.divideScalar(meshes.length); + const distance = avgPos.length() + 25; + const startPos = camera.position.clone(); + const endCamPos = new THREE.Vector3(avgPos.x, avgPos.y + distance * 0.4, avgPos.z + distance * 0.8); + const startTarget = controls.target.clone(); + const duration = 600; + const startTime = Date.now(); + function lerpHighlight() { + const elapsed = Date.now() - startTime; + const t = Math.min(elapsed / duration, 1); + const ease = 1 - Math.pow(1 - t, 3); + camera.position.lerpVectors(startPos, endCamPos, ease); + controls.target.lerpVectors(startTarget, avgPos, ease); + if (t < 1) requestAnimationFrame(lerpHighlight); + } + lerpHighlight(); + } + } } if (!data.success) return;