64 lines
1.6 KiB
TypeScript
64 lines
1.6 KiB
TypeScript
import { Vector } from "../geometry/vector.ts";
|
|
import { Frame, handleGIF } from "../processing/gif.ts";
|
|
|
|
type frame = { canvas: HTMLCanvasElement } & Frame;
|
|
export class GIFAnimation {
|
|
frames: frame[] = [];
|
|
canvas: HTMLCanvasElement;
|
|
ctx!: CanvasRenderingContext2D;
|
|
ready = false;
|
|
|
|
constructor(
|
|
url: string,
|
|
private origin: Vector,
|
|
private scale = 1,
|
|
) {
|
|
this.canvas = document.createElement("canvas");
|
|
|
|
this.init(url);
|
|
}
|
|
|
|
async init(url: string) {
|
|
const res = await fetch(url);
|
|
const buf = new Uint8Array(await res.arrayBuffer());
|
|
const gif = handleGIF(buf);
|
|
this.frames = gif.frames;
|
|
this.frameTimes = this.frames.map((f) => f.delay);
|
|
this.totalAnimationTime = this.frameTimes.reduce(
|
|
(a, b) => a + b,
|
|
0,
|
|
);
|
|
|
|
this.canvas.width = gif.w;
|
|
this.canvas.height = gif.h;
|
|
this.ctx = this.canvas.getContext("2d")!;
|
|
this.ready = true;
|
|
}
|
|
|
|
frameTimes!: number[];
|
|
totalAnimationTime = 0;
|
|
_frameCounter = 0;
|
|
currentFrameIndex = 0;
|
|
|
|
draw(timeSinceLastFrame: number) {
|
|
if (!this.ready) return;
|
|
this._frameCounter += timeSinceLastFrame;
|
|
|
|
const currentFrameDelay = this.frames[this.currentFrameIndex].delay * 10;
|
|
|
|
while (this._frameCounter >= currentFrameDelay) {
|
|
this._frameCounter -= currentFrameDelay;
|
|
this.currentFrameIndex = (this.currentFrameIndex + 1) %
|
|
this.frames.length;
|
|
}
|
|
|
|
const currentFrame = this.frames[this.currentFrameIndex];
|
|
doodler.drawImage(
|
|
currentFrame.canvas,
|
|
this.origin,
|
|
this.canvas.width * this.scale,
|
|
this.canvas.height * this.scale,
|
|
);
|
|
}
|
|
}
|