AnimationEventManager

by Matt

HTML

<base href="https://rawcdn.githack.com/mrdoob/three.js/r152/examples/" />
<script async src="https://cdn.jsdelivr.net/npm/[email protected]/dist/es-module-shims.js"></script>
<script type="importmap">
	{
		"imports": {
			"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
			"three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/",
			"lil-gui": "https://cdn.jsdelivr.net/npm/[email protected]/dist/lil-gui.esm.min.js"
		}
	}
</script>

CSS

canvas {
	position: fixed;
	inset: 0;
}

JavaScript

import * as THREE from 'three';

import Stats from 'three/addons/libs/stats.module.js';

let stats, clock;
let scene, camera, renderer, mixer;
let eventManager;


class AnimationEventManager {

	constructor(action) {
  	this.action = action
    this.clip = action.getClip()
    this.listeners = []
    this._prevTime = -Infinity
  }
  
  addTimeListener(time, callback) {
  	this.listeners.push({
      	time,
        callback
    })
  }
  
  update() {
  
  	const time = this.action.time
  
    const dir = Math.sign(time - this._prevTime)
    
    if (dir === -1) {
    	this._prevTime -= this.clip.duration
    }
    
  	for (const listener of this.listeners) {
    
    	if ( this._prevTime < listener.time && listener.time <= time) {
      
        listener.callback()
        
      }
    
    }
    
    this._prevTime = time
  }
  
}

init();
animate();

function init() {

  scene = new THREE.Scene();

  //

  camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
  camera.position.set( 25, 25, 50 );
  camera.lookAt( scene.position );

  //

  const axesHelper = new THREE.AxesHelper( 10 );
  scene.add( axesHelper );

  //

  const geometry = new THREE.BoxGeometry( 5, 5, 5 );
  const material = new THREE.MeshBasicMaterial( { color: 0xffffff, transparent: true } );
  const mesh = new THREE.Mesh( geometry, material );
  scene.add( mesh );

  // create a keyframe track (i.e. a timed sequence of keyframes) for each animated property
  // Note: the keyframe track type should correspond to the type of the property being animated

  // POSITION
  const positionKF = new THREE.VectorKeyframeTrack( '.position', [ 0, 1, 2 ], [ 0, 0, 0, 30, 0, 0, 0, 0, 0 ] );

  // SCALE
  const scaleKF = new THREE.VectorKeyframeTrack( '.scale', [ 0, 1, 2 ], [ 1, 1, 1, 2, 2, 2, 1, 1, 1 ] );

  // ROTATION
  // Rotation should be performed using quaternions, using a THREE.QuaternionKeyframeTrack
  // Interpolating Euler angles (.rotation property) can be...