瀏覽代碼

Added object details.

Mr.doob 7 月之前
父節點
當前提交
15371a1244
共有 5 個文件被更改,包括 243 次插入2 次删除
  1. 4 0
      devtools/background.js
  2. 55 0
      devtools/bridge.js
  3. 5 0
      devtools/content-script.js
  4. 37 0
      devtools/panel/panel.css
  5. 142 2
      devtools/panel/panel.js

+ 4 - 0
devtools/background.js

@@ -20,6 +20,10 @@ chrome.runtime.onConnect.addListener( port => {
 
 			chrome.tabs.sendMessage( tabId, message );
 
+		} else if ( message.name === 'request-object-details' && tabId ) {
+
+			chrome.tabs.sendMessage( tabId, message );
+
 		} else if ( tabId === undefined ) {
 
 			console.warn( 'Background: Message received from panel before init:', message );

+ 55 - 0
devtools/bridge.js

@@ -372,6 +372,10 @@
 
 				sendState();
 
+			} else if ( message.name === 'request-object-details' ) {
+
+				sendObjectDetails( message.uuid );
+
 			}
 
 		} );
@@ -400,6 +404,57 @@
 
 		}
 
+		function sendObjectDetails( uuid ) {
+
+			// Find the object with the given UUID
+			const findObjectInScenes = ( targetUuid ) => {
+
+				for ( const scene of observedScenes ) {
+
+					const found = scene.getObjectByProperty( 'uuid', targetUuid );
+					if ( found ) return found;
+
+				}
+
+				return null;
+
+			};
+
+			const object = findObjectInScenes( uuid );
+
+			if ( object ) {
+
+				const details = {
+					uuid: object.uuid,
+					type: object.type,
+					name: object.name,
+					position: {
+						x: object.position.x,
+						y: object.position.y,
+						z: object.position.z
+					},
+					rotation: {
+						x: object.rotation.x,
+						y: object.rotation.y,
+						z: object.rotation.z
+					},
+					scale: {
+						x: object.scale.x,
+						y: object.scale.y,
+						z: object.scale.z
+					}
+				};
+
+				dispatchEvent( 'object-details', details );
+
+			} else {
+
+				console.warn( 'DevTools: Object not found for UUID:', uuid );
+
+			}
+
+		}
+
 		function dispatchEvent( name, detail ) {
 
 			try {

+ 5 - 0
devtools/content-script.js

@@ -109,6 +109,11 @@ function handleBackgroundMessage( message ) {
 		message.id = 'three-devtools';
 		window.postMessage( message, '*' );
 
+	} else if ( message.name === 'request-object-details' ) {
+
+		message.id = 'three-devtools';
+		window.postMessage( message, '*' );
+
 	}
 
 }

+ 37 - 0
devtools/panel/panel.css

@@ -112,4 +112,41 @@ details.renderer-container > summary.renderer-summary::-webkit-details-marker {
 }
 details.renderer-container[open] > summary.renderer-summary .toggle-icon::before {
 	content: '▼'; /* Expanded */
+}
+
+/* Floating object details panel */
+.floating-details {
+	position: fixed;
+	z-index: 1000;
+	background: light-dark( #fff, #2a2a2a );
+	border: 1px solid light-dark( #ccc, #555 );
+	border-radius: 6px;
+	padding: 12px;
+	box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+	max-width: 300px;
+	min-width: 200px;
+	font-size: 11px;
+	pointer-events: none; /* Prevent interfering with mouse interactions */
+	opacity: 0;
+	transform: translateY(10px);
+	transition: opacity 0.2s ease, transform 0.2s ease;
+}
+
+.floating-details.visible {
+	opacity: 1;
+	transform: translateY(0);
+}
+
+.floating-details h4 {
+	margin: 8px 0 4px 0;
+	font-size: 10px;
+	text-transform: uppercase;
+	color: light-dark( #666, #aaa );
+	border-bottom: 1px solid light-dark( #eee, #444 );
+	padding-bottom: 2px;
+}
+
+.floating-details .property-row {
+	margin-bottom: 1px;
+	font-size: 10px;
 } 

+ 142 - 2
devtools/panel/panel.js

@@ -20,7 +20,7 @@ function createPropertyRow(label, value) {
 
 	const labelSpan = document.createElement('span');
 	labelSpan.className = 'property-label';
-	labelSpan.textContent = `${label}:`;
+	labelSpan.textContent = `${label}`;
 	labelSpan.style.marginRight = '10px';
 	labelSpan.style.whiteSpace = 'nowrap';
 
@@ -37,14 +37,34 @@ function createPropertyRow(label, value) {
 	return row;
 }
 
+function createVectorRow(label, vector) {
+	const row = document.createElement('div');
+	row.className = 'property-row';
+	row.style.marginBottom = '2px';
+	
+	// Pad label to ensure consistent alignment
+	const paddedLabel = label.padEnd(16, ' '); // Pad to 16 characters
+	const content = `${paddedLabel} ${vector.x.toFixed(3)}\t${vector.y.toFixed(3)}\t${vector.z.toFixed(3)}`;
+	row.textContent = content;
+	row.style.fontFamily = 'monospace';
+	row.style.whiteSpace = 'pre';
+	
+	return row;
+}
+
 // --- State ---
 const state = {
 	revision: null,
 	scenes: new Map(),
 	renderers: new Map(),
-	objects: new Map()
+	objects: new Map(),
+	selectedObject: null
 };
 
+// Floating details panel
+let floatingPanel = null;
+let mousePosition = { x: 0, y: 0 };
+
 
 // Create a connection to the background page
 const backgroundPageConnection = chrome.runtime.connect( {
@@ -80,6 +100,15 @@ backgroundPageConnection.onDisconnect.addListener( () => {
 
 } );
 
+// Function to request object details from the bridge
+function requestObjectDetails( uuid ) {
+	backgroundPageConnection.postMessage( {
+		name: 'request-object-details',
+		uuid: uuid,
+		tabId: chrome.devtools.inspectedWindow.tabId
+	} );
+}
+
 
 // Store renderer collapse states
 const rendererCollapsedState = new Map();
@@ -98,6 +127,13 @@ function clearState() {
 
 	}
 
+	// Hide floating panel
+	if ( floatingPanel ) {
+
+		floatingPanel.classList.remove( 'visible' );
+
+	}
+
 }
 
 // Listen for messages from the background page
@@ -139,6 +175,13 @@ function handleThreeEvent( message ) {
 
 			break;
 
+		// Handle object details response
+		case 'object-details':
+			state.selectedObject = message.detail;
+			console.log( 'Panel: Received object details:', message.detail );
+			showFloatingDetails( message.detail );
+			break;
+
 		// Handle a batch of objects for a specific scene
 		case 'scene':
 			const { sceneUuid, objects: batchObjects } = message.detail;
@@ -376,6 +419,13 @@ function renderObject( obj, container, level = 0 ) {
 	}
 
 	elem.innerHTML = labelContent;
+	
+	// Add mouseover handler to request object details
+	elem.addEventListener( 'mouseover', ( event ) => {
+		event.stopPropagation(); // Prevent bubbling to parent elements
+		requestObjectDetails( obj.uuid );
+	} );
+	
 	container.appendChild( elem );
 
 	// Handle children (excluding children of renderers, as properties are shown in details)
@@ -477,8 +527,98 @@ function updateUI() {
 
 	}
 
+
 }
 
+// Create floating details panel
+function createFloatingPanel() {
+
+	if ( floatingPanel ) return floatingPanel;
+
+	floatingPanel = document.createElement( 'div' );
+	floatingPanel.className = 'floating-details';
+	document.body.appendChild( floatingPanel );
+
+	return floatingPanel;
+
+}
+
+// Show floating details panel
+function showFloatingDetails( objectData ) {
+
+	const panel = createFloatingPanel();
+	
+	// Clear previous content
+	panel.innerHTML = '';
+	
+	if ( objectData.position ) {
+
+		panel.appendChild( createVectorRow( 'Position', objectData.position ) );
+
+	}
+
+	if ( objectData.rotation ) {
+
+		panel.appendChild( createVectorRow( 'Rotation', objectData.rotation ) );
+
+	}
+
+	if ( objectData.scale ) {
+
+		panel.appendChild( createVectorRow( 'Scale', objectData.scale ) );
+
+	}
+
+	// Position panel near mouse
+	updateFloatingPanelPosition();
+	
+	// Show panel
+	panel.classList.add( 'visible' );
+
+}
+
+// Update floating panel position
+function updateFloatingPanelPosition() {
+
+	if ( ! floatingPanel || ! floatingPanel.classList.contains( 'visible' ) ) return;
+
+	const offset = 15; // Offset from cursor
+	let x = mousePosition.x + offset;
+	let y = mousePosition.y + offset;
+
+	// Prevent panel from going off-screen
+	const panelRect = floatingPanel.getBoundingClientRect();
+	const maxX = window.innerWidth - panelRect.width - 10;
+	const maxY = window.innerHeight - panelRect.height - 10;
+
+	if ( x > maxX ) x = mousePosition.x - panelRect.width - offset;
+	if ( y > maxY ) y = mousePosition.y - panelRect.height - offset;
+
+	floatingPanel.style.left = `${Math.max( 10, x )}px`;
+	floatingPanel.style.top = `${Math.max( 10, y )}px`;
+
+}
+
+// Track mouse position
+document.addEventListener( 'mousemove', ( event ) => {
+
+	mousePosition.x = event.clientX;
+	mousePosition.y = event.clientY;
+	updateFloatingPanelPosition();
+
+} );
+
+// Hide panel when mouse leaves the tree area
+document.addEventListener( 'mouseover', ( event ) => {
+
+	if ( floatingPanel && ! event.target.closest( '.tree-item' ) ) {
+
+		floatingPanel.classList.remove( 'visible' );
+
+	}
+
+} );
+
 // Initial UI update
 clearState();
 updateUI();

粤ICP备19079148号