ThreeJS问题咨询:renderer.setSize执行顺序异常与Canvas动态适配需求
renderer.setSize() run before earlier code in Three.js? And how to fit Canvas to Plane dimensions dynamically? Hey there! Let's tackle your questions one by one—this is a super common gotcha with asynchronous code in JavaScript, so you're not alone here.
1. Why does renderer.setSize() execute before your model loading logic?
The core issue here is asynchronous operations. When you use a loader (like GLTFLoader or OBJLoader) to fetch a model from local storage (or any external source), the load() method doesn't block the rest of your code from running. Instead:
- Your initial
renderer.setSize(800, 600)runs immediately because it's synchronous code. - The loader sends a request to fetch the model, but while it waits for that request to complete, JavaScript moves on to execute the next lines of code (which might include another unintended
renderer.setSize()call). - Only once the model finishes loading does the loader's
onLoadcallback fire—by then, your earliersetSizehas already run.
It's not that setSize is "out of order"—it's just that asynchronous tasks don't follow the linear top-to-bottom flow you might expect for synchronous code.
2. How to dynamically resize Canvas to match loaded Plane dimensions
To fix this, you need to move your renderer.setSize() logic inside the model's load completion callback. That way, you only adjust the Canvas size after you have the Plane's actual dimensions. Here's a concrete example:
Before (the problematic order):
// Initial Canvas setup (runs first) const renderer = new THREE.WebGLRenderer(); renderer.setSize(800, 600); document.body.appendChild(renderer.domElement); // Model loading (asynchronous, runs in background) const loader = new THREE.GLTFLoader(); loader.load('path/to/your-plane.gltf', function(gltf) { const plane = gltf.scene.children.find(child => child instanceof THREE.Mesh); // This code runs LATER, after setSize already executed }); // Accidental extra setSize that runs before model loads renderer.setSize(800, 600); // Oops, this runs immediately too!
After (fixed, size adjusts after model loads):
// Initial Canvas setup (temporary size) const renderer = new THREE.WebGLRenderer(); renderer.setSize(800, 600); // Temporary initial size document.body.appendChild(renderer.domElement); const camera = new THREE.PerspectiveCamera(75, 800/600, 0.1, 1000); camera.position.z = 5; const scene = new THREE.Scene(); // Model loading (wait for this to finish before resizing) const loader = new THREE.GLTFLoader(); loader.load('path/to/your-plane.gltf', function(gltf) { const plane = gltf.scene.children.find(child => child instanceof THREE.Mesh); scene.add(plane); // Now we have the Plane's actual dimensions! const planeWidth = plane.scale.x * plane.geometry.parameters.width; const planeHeight = plane.scale.y * plane.geometry.parameters.height; // Resize renderer to match Plane dimensions renderer.setSize(planeWidth, planeHeight); // Update camera aspect ratio to match new Canvas size camera.aspect = planeWidth / planeHeight; camera.updateProjectionMatrix(); // Render the scene with the new size renderer.render(scene, camera); }, undefined, function(error) { console.error('Error loading model:', error); }); // Your animation loop (if you have one) function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate();
Key points here:
- We only call
renderer.setSize()after we've accessed the loaded Plane's dimensions. - Don't forget to update the camera's
aspectratio and callcamera.updateProjectionMatrix()—otherwise, your scene will look stretched. - If you're using a different loader (like creating
PlaneBufferGeometrydirectly instead of loading a model), the same logic applies: wait until the geometry is ready before adjusting the Canvas size.
内容的提问来源于stack exchange,提问作者Yone

