stage-js
Version:
2D HTML5 Rendering and Layout
145 lines (118 loc) • 3.31 kB
text/typescript
import { math } from "../common/math";
import { Texture, TextureSelectionInputArray, texture } from "../texture";
import { Component } from "./component";
export function anim(frames: string | TextureSelectionInputArray, fps?: number) {
const anim = new Anim();
anim.frames(frames).gotoFrame(0);
fps && anim.fps(fps);
return anim;
}
// TODO: replace with atlas fps or texture time
/** @internal */ const FPS = 15;
export class Anim extends Component {
/** @internal */ _texture: Texture | null = null;
/** @internal */ _frames: Texture[] = [];
/** @internal */ _fps: number;
/** @internal */ _ft: number;
/** @internal */ _time: number = -1;
/** @internal */ _repeat: number = 0;
/** @internal */ _index: number = 0;
/** @internal */ _callback: () => void;
constructor() {
super();
this.label("Anim");
this._fps = FPS;
this._ft = 1000 / this._fps;
this.tick(this._animTick, false);
}
/** @hidden */
renderTexture(context: CanvasRenderingContext2D) {
if (!this._texture) return;
this._texture.draw(context);
}
/** @internal */
private _animTickLastTime = 0;
/** @internal */
private _animTick = (t: number, now: number, last: number) => {
if (this._time < 0 || this._frames.length <= 1) {
return;
}
// ignore old elapsed
const ignore = this._animTickLastTime != last;
this._animTickLastTime = now;
if (ignore) {
return true;
}
this._time += t;
if (this._time < this._ft) {
return true;
}
const n = (this._time / this._ft) | 0;
this._time -= n * this._ft;
this.moveFrame(n);
if (this._repeat > 0 && (this._repeat -= n) <= 0) {
this.stop();
this._callback && this._callback();
return false;
}
return true;
};
fps(fps?: number) {
if (typeof fps === "undefined") {
return this._fps;
}
this._fps = fps > 0 ? fps : FPS;
this._ft = 1000 / this._fps;
return this;
}
/** @deprecated Use frames */
setFrames(frames: string | TextureSelectionInputArray) {
return this.frames(frames);
}
frames(frames: string | TextureSelectionInputArray) {
this._index = 0;
this._frames = texture(frames).array();
this.touch();
return this;
}
length() {
return this._frames ? this._frames.length : 0;
}
gotoFrame(frame: number, resize = false) {
this._index = math.wrap(frame, this._frames.length) | 0;
resize = resize || !this._texture;
this._texture = this._frames[this._index];
if (resize) {
this.pin("width", this._texture.getWidth());
this.pin("height", this._texture.getHeight());
}
this.touch();
return this;
}
moveFrame(move: number) {
return this.gotoFrame(this._index + move);
}
repeat(repeat: number, callback?: () => void) {
this._repeat = repeat * this._frames.length - 1;
this._callback = callback;
this.play();
return this;
}
play(frame?: number) {
if (typeof frame !== "undefined") {
this.gotoFrame(frame);
this._time = 0;
} else if (this._time < 0) {
this._time = 0;
}
this.touch();
return this;
}
stop(frame?: number) {
this._time = -1;
if (typeof frame !== "undefined") {
this.gotoFrame(frame);
}
return this;
}
}