js/Mobilizing/renderer/3D/three/texture/ImageSequenceTexture.js
import Component from "../../../../core/Component";
import Timer from "../../../../time/Timer";
import * as debug from "../../../../core/util/Debug";
/**
* The ImageSequenceTexture represents a special kind of 2D Texture that is built from a sequence of pictures that you can play at a given rate and with a given play mode.
*
* @class ImageSequenceTexture
*/
const MODE_FORWARD = "forward";
const MODE_BACKWARD = "backward";
const MODE_PINGPONG = "pingpong";
const MODE_RANDOM = "random";
export default class ImageSequenceTexture extends Component {
/**
@param {Object} params Parameters object, given by the constructor.
@param {Number} params.fps the frame per second of this sequence
@param {String} params.sequenceMode the mode of playing for this sequence. One of "forward", "backward", "pingpong", "random".
@param {Number} params.direction the direction of the play head. 1 for forward, -1 for backward
@param {Number} params.lapsToDo the number of loop to do before to stop the animation
@param {Array <Texture>} params.textures the texture array that make the image sequence
*/
constructor({
fps = 10,
sequenceMode = MODE_FORWARD,
direction = 1,
lapsToDo = -1,
textures = [],
} = {}) {
super(...arguments);
this.sequenceMode = sequenceMode;
this.direction = direction;
this.textures = textures;
this.frameRate = fps;
this.lapsToDo = lapsToDo;
this.frameCount = this.textures.length - 1;
this.playing = false;
this.laps = 0;
this.currentFrame = 0;
this.timer = new Timer({
"interval": 1000 / this.frameRate,
"callback": this.updateSequence.bind(this),
});
this.chain(this.timer); //chainning system
}
setup() {
super.setup();
this.enterFrame = 0;
this.currentFrame = 0;
this.outFrame = this.frameCount;
this.timer.on();
this.timer.start();
}
/**
Set the starting frame of the current loaded sequence.
@param {Integer} frame starting frame.
*/
setEnterFrame(frame) {
if (frame >= 0 && frame < this.frameCount) {
this.enterFrame = frame;
this.currentFrame = this.enterFrame;
}
else {
this.enterFrame = 0;
this.currentFrame = 0;
debug.log("ERROR enterFrame %i is out of sequence bounds - back to 0", frame);
}
}
/**
Set the ending frame of the current loaded sequence.
@param {Integer} frame ending frame.
*/
setOutFrame(frame) {
if (frame >= this.enterFrame && frame < this.frameCount) {
this.outFrame = frame;
}
else {
this.outFrame = this.frameCount - 1;
debug.log("ERROR outFrame %i is out of sequence bounds - back to frameCount", frame);
}
}
/**
Set the current frame to force the play head, can be used on pause to manually control the play head
@event frameUpdate triggered by this component's inner events
*/
setCurrentFrame(frame) {
this.currentFrame = frame;
this.events.trigger("frameUpdate");
}
/**
Set the sequence playing mode.
@param {String} mode playing mode : "forward", "backward", "pingpong", "random".
*/
setMode(mode) {
this.sequenceMode = mode;
}
/**
update the Image sequence. You have to call this once per frame for the Image sequence Texture to update and run its own logic.
@event frameUpdate triggered by this component's inner events
*/
updateSequence() {
if (!this.playing) {
return;
}
if (this.sequenceMode === MODE_FORWARD) {
if (this.laps >= 0 && this.laps < this.lapsToDo) {
this.currentFrame++;
this.currentFrame %= this.outFrame + 1;
if (this.currentFrame === 0) {
this.currentFrame = this.enterFrame;
}
if (this.currentFrame === this.outFrame) {
this.laps++;
}
}
else if (this.lapsToDo < 0) {
this.currentFrame++;
this.currentFrame %= this.outFrame + 1;
if (this.currentFrame === 0) {
this.currentFrame = this.enterFrame;
}
}
if (this.lapsToDo === this.laps) {
this.stop();
}
debug.log(`currentFrame = ${this.currentFrame}`);
}
else if (this.sequenceMode === MODE_BACKWARD) {
if (this.laps >= 0 && this.laps < this.lapsToDo) {
this.currentFrame--;
if (this.currentFrame < this.enterFrame) {
this.currentFrame = this.outFrame;
}
if (this.currentFrame === this.enterFrame) {
this.laps++;
}
}
else if (this.lapsToDo < 0) {
this.currentFrame--;
if (this.currentFrame < this.enterFrame) {
this.currentFrame = this.outFrame;
}
}
if (this.lapsToDo === this.laps) {
this.stop();
}
debug.log(`currentFrame = ${this.currentFrame}`);
}
else if (this.sequenceMode === MODE_PINGPONG) {
if (this.laps >= 0 && this.laps < this.lapsToDo) {
this.currentFrame += this.direction;
//protection against border over
if (this.currentFrame < 0) {
this.currentFrame = 0;
}
if (this.currentFrame > this.outFrame) {
this.currentFrame = this.outFrame;
}
if (this.currentFrame >= this.outFrame) {
this.direction = -this.direction;
this.laps++;
}
if (this.currentFrame <= this.enterFrame) {
this.direction = -this.direction;
this.laps++;
}
}
else if (this.lapsToDo < 0) {
this.currentFrame += this.direction;
if (this.currentFrame >= this.outFrame) {
this.direction = -this.direction;
}
if (this.currentFrame <= this.enterFrame) {
this.direction = -this.direction;
}
}
if (this.lapsToDo === this.laps) {
this.stop();
}
debug.log(`currentFrame = ${this.currentFrame}`);
}
else if (this.sequenceMode === MODE_RANDOM) {
this.currentFrame = Math.floor(Math.random() * (this.outFrame - this.enterFrame) + this.enterFrame);
debug.log(`currentFrame = ${this.currentFrame}`);
}
this.events.trigger("frameUpdate");
}
/**
Get the current Image Sequence frame Texture
@return {Texture} the texture
*/
getCurrentTexture() {
return this.textures[this.currentFrame];
}
/**
play the Image Sequence.
*/
play() {
this.playing = true;
}
/**
pause the Image Sequence.
*/
pause() {
this.playing = false;
}
/**
stop the Image Sequence. The image frame is resetted.
*/
stop() {
this.currentFrame = 0;
this.playing = false;
this.laps = 0;
}
/**
Loop the image sequence for a given number of iterations (once play() is called).
@param {Number} count : -1 means infinity.
*/
loop(count) {
this.lapsToDo = count;
}
}