通过下拉菜单动态加载3D模型至容器的JS实现问题
问题分析与修复方案
核心问题点
- 变量作用域错误:通过
javascript:伪协议定义的var model是局部变量,无法被全局的Three.js脚本访问;且URL字符串未加引号,存在语法错误。 - 硬编码占位符未替换:
loadOBJ函数中直接使用字符串'$model',而非引用实际变量。 - CSS选择器缺失类前缀:样式中
dropbtn、dropdown等选择器未加.,导致样式完全不生效。 - 模型切换未清理旧实例:切换模型时未移除场景中的旧模型,会导致内存泄漏和渲染异常。
修复后的完整代码
<style> /* 修复CSS选择器,添加类前缀 */ .dropbtn { background-color: #04AA6D; color: white; padding: 16px; font-size: 16px; border: none; } .dropdown { position: relative; display: inline-block; } .dropdown-content { display: none; position: absolute; background-color: #f1f1f1; min-width: 160px; box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); z-index: 1; } .dropdown-content a { color: black; padding: 12px 16px; text-decoration: none; display: block; } .dropdown-content a:hover {background-color: #ddd;} .dropdown:hover .dropdown-content {display: block;} .dropdown:hover .dropbtn {background-color: #3e8e41;} </style> <body> <div class="dropdown"> <button class="dropbtn">Model</button> <div class="dropdown-content"> <!-- 绑定点击事件,调用全局切换函数,传递URL --> <a href="javascript:switchModel('https://s3-us-west-2.amazonaws.com/s.cdpn.io/127738/banana.obj')">Banana Model</a> <a href="javascript:switchModel('https://s3-us-west-2.amazonaws.com/s.cdpn.io/127738/another-model.obj')">Another Model</a> </div> </div> <canvas id="scene"></canvas> <script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.min.js'></script> <script src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/127738/OBJLoader.js'></script> <script> var renderer, scene, camera, currentModel; var ww = window.innerWidth, wh = window.innerHeight; function init(){ renderer = new THREE.WebGLRenderer({canvas : document.getElementById('scene')}); renderer.setSize(ww,wh); scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(50,ww/wh, 0.1, 10000 ); camera.position.set(0,0,500); scene.add(camera); // 添加光源 const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 ); directionalLight.position.set( 0, 0, 350 ); directionalLight.lookAt(new THREE.Vector3(0,0,0)); scene.add( directionalLight ); // 初始化加载默认模型 loadOBJ('https://s3-us-west-2.amazonaws.com/s.cdpn.io/127738/banana.obj'); } // 封装加载模型函数,接收URL参数 function loadOBJ(modelUrl){ const manager = new THREE.LoadingManager(); const loader = new THREE.OBJLoader( manager ); loader.load(modelUrl, addModelInScene); } // 添加模型到场景,并处理旧模型清理 function addModelInScene(object){ // 移除旧模型并释放内存 if(currentModel){ scene.remove(currentModel); currentModel.traverse(child => { if(child instanceof THREE.Mesh){ child.geometry.dispose(); child.material.dispose(); } }); } currentModel = object; // 模型位置与旋转设置 currentModel.rotation.x = Math.PI/2; currentModel.position.y = -200; currentModel.position.z = 50; // 处理模型材质 object.traverse( function ( child ) { if(child instanceof THREE.Mesh){ child.material.color = new THREE.Color(0X00FF00); child.geometry.computeVertexNormals(); } }); scene.add(currentModel); // 启动渲染(避免重复调用) if(!render.isRunning){ render(); } } // 全局切换模型函数,供下拉菜单调用 function switchModel(modelUrl){ loadOBJ(modelUrl); } let render = function () { render.isRunning = true; requestAnimationFrame(render); if(currentModel){ currentModel.rotation.z += .01; } renderer.render(scene, camera); }; init(); </script> </body>
关键修改说明
- 修复变量作用域:定义全局函数
switchModel,下拉菜单通过该函数直接传递模型URL,避免局部变量无法访问的问题。 - 动态加载模型:
loadOBJ函数改为接收URL参数,替换原硬编码的占位符,实现模型动态切换。 - 修复CSS样式:为所有类选择器添加
.前缀,确保下拉菜单样式正常显示。 - 清理旧模型:切换模型时移除场景中的旧模型,并释放几何体和材质内存,防止内存泄漏。
- 优化渲染逻辑:渲染函数中判断模型是否存在,避免空指针错误;标记渲染状态,防止重复启动渲染循环。
内容的提问来源于stack exchange,提问作者repeekyraid cero
相关产品推荐
相关产品推荐

