How to manually control animation frame by frame?

Viewed 3647

Regarding this Three.js example https://threejs.org/examples/#webgl_animation_keyframes I was able to load my own model and play animation in a loop. However I want to make a "slider" that will allow user to control animation frame by frame. How I can achieve this with AnimationMixer? Let's say that animation clip has duration 4s. I would like to control in realtime current animation time from 0s to 4s.

3 Answers

The other answer is incomplete. It would never work with a slider input as described in the original post. The problem is that the Animation Mixer update function changes the time relative to the current time and is subsequently unsuitable for a slider input as described.

Instead you want to make a function like this:

  function seekAnimationTime(animMixer, timeInSeconds){
    animMixer.time=0;
    for(var i=0;i<animMixer._actions.length;i++){
      animMixer._actions[i].time=0;
    }
    animMixer.update(timeInSeconds)
  }

With this you could then run this in your slider OnChange event listener:

seekAnimationTime(yourAnimationMixer, event.value);

Or to set the animation time to 2s:

seekAnimationTime(yourAnimationMixer,2);

This solution will work regardless of what the previous animationMixer time was.

The answer is right there in the code of the example you linked:

mixer = new THREE.AnimationMixer( model );
mixer.clipAction( gltf.animations[ 0 ] ).play();

// ...

function animate() {
    var delta = clock.getDelta();
    mixer.update( delta );

    renderer.render( scene, camera );

    requestAnimationFrame( animate );
}

mixer.update( delta ); is what updates the animation by 0.0166 seconds on every frame (at 60FPS). If you want to jump to a specific moment, simply assign the .time property to whatever second you need.

See here for more documentation on AnimationMixer.

Alexander's answer is correct; to achieve what is described with a slider input control (i.e. like scrubbing a video) then the AnimationMixer.update method is not appropriate.

Fortunately, there now exists a AnimationMixer.setTime method to accomplish this. It is implemented essentially as Alexander described, which you can see here: https://github.com/mrdoob/three.js/blob/r130/src/animation/AnimationMixer.js#L649-L661

Scrub/slide to an exact time in seconds:

mixer.setTime( exactTimeInSeconds )

Move forward in time by delta amount of seconds:

Using the Clock.getDelta method to get seconds passed since last call:

const delta = clock.getDelta(); // Optional: In this case we get delta from Three.js' own clock instead of a slider
mixer.update( delta );

All Together

To tie this all together, consider an example scenario where we want to get to the 3 second mark:

mixer.setTime(2); // time = 2 second mark
mixer.update(1); // time = 2 + 1 = 3 second mark
Related