使用zIndex在Three.js上层显示2D调试面板的可行性咨询
基于React的Three.js项目中2D调试面板显示问题
我有一个基于React的Three.js项目,需要在Three.js画面上方显示用于调试参数的2D面板。我原本的思路是创建div元素,通过zIndex属性让它显示在WebGL内容前面,但实际面板没显示出来。想请教这个思路是否正确,还是应该在WebGL内部制作面板?
源代码
import React,{useRef,useState,useEffect} from 'react' import axios from 'axios'; import styles from "../css/test-styles.module.css"; import * as THREE from 'three'; import TrackballControls from 'three-trackballcontrols'; import three_test from "../images/three_test.jpg"; function ThreeDebugInfo(){ return <div style={{width:"500px",zIndex:100}}>testtesttest</div> } function GlCanvas() { const refContainer = useRef(null); useEffect(() => { // === THREE.JS CODE START === var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000); var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth /100 * 80, window.innerHeight / 100 * 80); // document.body.appendChild( renderer.domElement ); // use ref as a mount point of the Three.js scene instead of the document.body refContainer.current && refContainer.current.appendChild( renderer.domElement ); camera.position.z = -500; const loadTex = async () =>{ const texLoader = new THREE.TextureLoader(); var texture = new THREE.TextureLoader().load(three_test); console.log("check texture is loaded:",texture); var ground_geo = new THREE.PlaneGeometry( 3309, 2339, 8, 8 ); ground_geo.translate(0, 0, -50); var ground_mat = new THREE.MeshBasicMaterial({ map: texture }); var ground = new THREE.Mesh( ground_geo, ground_mat ); //ground.rotation.x = Math.PI / -2; scene.add( ground ); var cube_geo = new THREE.BoxGeometry(100, 100, 100); //cube_geo.translate(25, 0, 25); var cube_mat = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); var cube = new THREE.Mesh(cube_geo, cube_mat); scene.add(cube); const controls = new TrackballControls(camera, renderer.domElement); camera.position.z = 5; controls.rotateSpeed = 5.0; //回転速度 controls.zoomSpeed = 1.2; controls.panSpeed = 0.3; controls.update(); const animate = function () { requestAnimationFrame(animate); // Required for updating during animations. controls.update(); renderer.render(scene, camera); } animate(); } loadTex(); }, []); return ( <div style={{zIndex:1}} ref={refContainer}></div> ); } function ThreePage() { return ( <React.Fragment> <GlCanvas></GlCanvas> <ThreeDebugInfo></ThreeDebugInfo> </React.Fragment> ); } export default ThreePage
解决方案
你的思路完全正确,用HTML的2D元素做调试面板是更高效的选择,问题出在zIndex的生效条件上:zIndex只对设置了定位(position: relative/absolute/fixed/sticky)的元素生效,你当前的ThreeDebugInfo组件里的div没有设置定位,所以zIndex不生效,导致面板被Three.js的canvas元素覆盖。
修改步骤:
- 给
ThreeDebugInfo的div添加定位样式,同时可以增加背景色避免和3D内容重叠:
function ThreeDebugInfo(){ return <div style={{ width:"500px", zIndex:100, position: 'absolute', top: 20, left: 20, backgroundColor: 'rgba(255,255,255,0.8)', padding: '10px' }}>testtesttest</div> }
- 移除
GlCanvas返回div上无效的zIndex(因为没有定位,这个zIndex不会起作用):
return ( <div ref={refContainer}></div> );
额外说明:
完全没必要在WebGL内部制作调试面板——WebGL里做UI需要额外处理渲染、交互事件,开发和维护成本都很高。用原生HTML/CSS做调试面板不仅简单,还能直接集成dat.GUI、lil-gui这类成熟的调试库,直接把GUI实例挂载到这个div里就能快速实现参数调试功能。
内容的提问来源于stack exchange,提问作者whitebear
相关产品推荐
相关产品推荐

