Three.js多OBJ模型加载至不同div异常:均显示于最后一个容器
问题原因与解决方案
这个问题我太熟悉啦!核心原因是JavaScript闭包的异步陷阱,咱们一步步拆解:
为什么会出现这个问题?
- 你在for循环里用
var声明了scene变量,var是函数级作用域,整个循环过程中只有一个scene的引用在被复用。 OBJLoader.load()是异步操作——它会先发起请求,等模型文件下载完成后才会执行回调函数。但循环的执行速度远快于网络请求,当回调触发时,循环早就跑完了,此时scene已经指向最后一次循环创建的场景对象。所以不管哪个模型加载完成,都会被添加到最后一个场景里,自然就都显示在最后一个div对应的画布中了。
三种快速修复方案
方案1:改用let声明scene(ES6+推荐)
把循环里的var scene = new THREE.Scene();改成let scene = new THREE.Scene();。let是块级作用域,每次循环都会创建一个独立的scene变量,回调函数会捕获当前迭代对应的那个scene引用,这样每个模型就会被添加到正确的场景中:
for( var _key in models ){ let scene = new THREE.Scene(); // 这里改成let var element = document.createElement( "div" ); element.className = "list-item"; element.innerHTML = template.replace( '$', _key); console.log('does this work', element.innerHTML); scene.userData.element = element.querySelector( ".scene" ); content.appendChild( element ); var camera = new THREE.PerspectiveCamera( 50, 1, 1, 10 ); camera.position.z = 2; scene.userData.camera = camera; var objLoader = new THREE.OBJLoader(); objLoader.load( models[_key].obj, function ( object ) { scene.add( object ); console.log(object); }); }
方案2:用立即执行函数(IIFE)封装作用域
如果需要兼容不支持ES6的旧环境,可以用立即执行函数把每次迭代的scene变量封装成独立作用域:
for( var _key in models ){ (function(currentScene) { var element = document.createElement( "div" ); element.className = "list-item"; element.innerHTML = template.replace( '$', _key); console.log('does this work', element.innerHTML); currentScene.userData.element = element.querySelector( ".scene" ); content.appendChild( element ); var camera = new THREE.PerspectiveCamera( 50, 1, 1, 10 ); camera.position.z = 2; currentScene.userData.camera = camera; var objLoader = new THREE.OBJLoader(); objLoader.load( models[_key].obj, function ( object ) { currentScene.add( object ); console.log(object); }); })(new THREE.Scene()); // 每次循环传入新创建的场景 }
方案3:改用forEach遍历模型键
forEach的回调函数本身就是独立作用域,每个迭代的变量都是独立的,也能避开闭包陷阱:
Object.keys(models).forEach(_key => { let scene = new THREE.Scene(); var element = document.createElement( "div" ); element.className = "list-item"; element.innerHTML = template.replace( '$', _key); console.log('does this work', element.innerHTML); scene.userData.element = element.querySelector( ".scene" ); content.appendChild( element ); var camera = new THREE.PerspectiveCamera( 50, 1, 1, 10 ); camera.position.z = 2; scene.userData.camera = camera; var objLoader = new THREE.OBJLoader(); objLoader.load( models[_key].obj, function ( object ) { scene.add( object ); console.log(object); }); });
只要修复了闭包问题,每个模型就会被正确添加到对应的场景中,自然就能显示在各自的div里啦。
内容的提问来源于stack exchange,提问作者emily
相关产品推荐
相关产品推荐

