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

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 mixer as 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

  1. Global Mixer & Clock: We moved mixer to the global scope and added a THREE.Clock to track time between frames—this lets us pass the correct delta time to mixer.update() each frame.
  2. 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.
  3. Scene Order: We added the mesh to the scene before setting up animations (though this isn't strictly required, it's a cleaner practice).
  4. Simplified Render Loop: Combined the separate animate() and render() 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:48:15