itowns
Version:
A JS/WebGL framework for 3D geospatial data visualization
142 lines (132 loc) • 3.29 kB
JavaScript
import * as THREE from 'three';
const FRAMERATE = 60;
const FRAME_DURATION = 1000 / FRAMERATE;
// player statut
const PLAYER_STATE = {
// player is stopped
STOP: 0,
// player plays animation
PLAY: 1,
// player is at the end of an animation
END: 2,
// player is paused
PAUSE: 3
};
// Private functions
// stop timer and re-init parameter
const resetTimer = function (player) {
if (player.id) {
clearInterval(player.id);
player.id = undefined;
}
if (player.waitTimer) {
clearInterval(player.waitTimer);
player.waitTimer = undefined;
}
player.keyframe = 0;
};
// finish animation and re-init parameter
const finishAnimation = function (player) {
resetTimer(player);
if (player.isEnded()) {
player.dispatchEvent({
type: 'animation-ended'
});
}
player.dispatchEvent({
type: 'animation-stopped'
});
player.duration = 0;
};
/**
* It can play, pause or stop Animation or AnimationExpression (See below).
* AnimationPlayer is needed to use Animation or AnimationExpression
* AnimationPlayer emits events :
* - for each animation's frame;
* - when Animation is stopped
* - when Animation is ending
*/
class AnimationPlayer extends THREE.EventDispatcher {
constructor() {
super();
this.id = null;
this.keyframe = 0;
this.duration = 0;
this.state = PLAYER_STATE.STOP;
this.waitTimer = null;
this.callback = () => {};
}
isPlaying() {
return this.state === PLAYER_STATE.PLAY;
}
isStopped() {
return this.state === PLAYER_STATE.STOP;
}
isEnded() {
return this.state === PLAYER_STATE.END;
}
// Public functions
/**
* Set the Player `callback` property. This callback is executed at each animation frame.
*
* @param {function} callback - The callback to execute at each animation frame.
*/
setCallback(callback) {
this.callback = callback;
}
/**
* Play one animation.
* If another animation is playing, it's stopped and the new animation is played.
*
* @param {number} duration - The duration to play
*/
play(duration) {
this.duration = duration;
this.dispatchEvent({
type: 'animation-started'
});
this.state = PLAYER_STATE.PLAY;
resetTimer(this);
this.id = setInterval(this.frame.bind(this), FRAME_DURATION);
}
/**
* Play an animation after a number of frames.
*
* @param {number} duration The duration to play
* @param {number} waitingFrame The waiting time before start animation (time in frame)
*/
playLater(duration, waitingFrame) {
const timew = Math.floor(FRAME_DURATION * waitingFrame);
window.clearInterval(this.waitTimer);
const self = this;
this.waitTimer = window.setTimeout(() => {
self.play(duration);
}, timew);
}
/**
* Stop the current animation.
*
*/
stop() {
this.state = PLAYER_STATE.STOP;
finishAnimation(this);
}
/**
* Executed for each frame.
*
* @private
*/
frame() {
if (this.keyframe < this.duration) {
this.keyframe++;
this.dispatchEvent({
type: 'animation-frame'
});
this.callback();
} else {
this.state = PLAYER_STATE.END;
finishAnimation(this);
}
}
}
export default AnimationPlayer;