You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:30:34