Three.js中AnimationMixer无法播放GLB文件动画的问题
Fixing GLB Animation Playback in Three.js
Let's get those "Intro" and "Rotate" animations playing properly. I've identified two main issues in your code that are blocking the animation from running:
Key Issues
- AnimationMixer Scope: You declared
mixeras a local variable inside the GLTFLoader callback, so your animation loop can't access it to update the mixer each frame. - Missing Mixer Update: Three.js AnimationMixer requires you to call
mixer.update(delta)every frame to advance the animation timeline—your current code doesn't do this.
Fixed Code
Here's the revised version with all necessary fixes:
var scene, renderer; var camera; var mesh; var clickDown; var model; var isMouseDown = false; var mixer; // Declare mixer in global scope so animation loop can access it var clock = new THREE.Clock(); // Clock to calculate frame time delta function execFA() { scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.z = 25; camera.position.y = 15; renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.getElementById('container').appendChild(renderer.domElement); renderer.setClearColor(0x00ffff, 1); renderer.gammaOutput = true; var light = new THREE.DirectionalLight("#c1582d", 1); var ambient = new THREE.AmbientLight("#85b2cd"); light.position.set(0, -70, 100).normalize(); scene.add(light); scene.add(ambient); var loader = new THREE.GLTFLoader(); // Load a glTF resource loader.load( './models/vc.glbf', function(gltf) { model = gltf; mesh = gltf.scene; mesh.scale.set(3, 3, 3); scene.add(mesh); // Add mesh to scene first before setting up animations // Initialize AnimationMixer with the model's scene mixer = new THREE.AnimationMixer(mesh); // Play all animations (or target specific ones) gltf.animations.forEach(animation => { var action = mixer.clipAction(animation); action.play(); // If you want "Intro" to play once and then "Rotate" loop, uncomment below: // if (animation.name === 'Intro') { // action.setLoop(THREE.LoopOnce, THREE.Infinity); // action.clampWhenFinished = true; // } }); animate(); }, function(xhr) { console.log((xhr.loaded / xhr.total * 100) + '% loaded'); }, function(error) { console.log('An error happened', error); } ); document.addEventListener("mousedown", onMouseDown); document.addEventListener("touchstart", onMouseDown); document.addEventListener("mouseup", onMouseUp); document.addEventListener("touchend", onMouseUp); document.addEventListener("mousemove", onMouseMove); document.addEventListener("touchmove", onMouseMove); } function animate() { requestAnimationFrame(animate); // Calculate time delta since last frame var delta = clock.getDelta(); // Update the mixer if it exists if (mixer) { mixer.update(delta); } renderer.render(scene, camera); } function onMouseDown(event) { isMouseDown = true; } function onMouseMove(event) { if (isMouseDown && mesh) { mesh.rotation.y = getMouseX(event) / 50; mesh.rotation.x = getMouseY(event) / 50; } } function onMouseUp(event) { isMouseDown = false; } function getMouseX(event) { return event.type.indexOf("touch") === -1 ? event.clientX : event.touches[0].clientX; } function getMouseY(event) { return event.type.indexOf("touch") === -1 ? event.clientY : event.touches[0].clientY; } window.addEventListener('DOMContentLoaded', execFA);
Critical Changes Explained
- Global Mixer & Clock: We moved
mixerto the global scope and added aTHREE.Clockto track time between frames—this lets us pass the correct delta time tomixer.update()each frame. - Mixer Update in Animation Loop: The
animate()function now calculates the time delta and updates the mixer every frame, which is essential for driving the animation. - Scene Order: We added the mesh to the scene before setting up animations (though this isn't strictly required, it's a cleaner practice).
- Simplified Render Loop: Combined the separate
animate()andrender()functions into a single loop for better consistency—Three.js works best with a single requestAnimationFrame loop that handles both updates and rendering.
If you want to play only specific animations (e.g., "Intro" once, then "Rotate" on loop), you can uncomment the conditional logic in the animation forEach loop to set loop modes and clamp behavior.
内容的提问来源于stack exchange,提问作者mohammad mahmoodi
相关产品推荐
相关产品推荐

