AnimationTool/app/frame_handler.ts

223 lines
6.8 KiB
TypeScript
Raw Normal View History

2019-09-27 00:45:28 +00:00
import { IAnimationData } from './Interfaces/IAnimationData';
import { IFramePinData } from './Interfaces/IFramePinData';
2019-10-01 04:58:04 +00:00
import { IProjectData } from './Interfaces/IProjectData';
2019-09-27 00:45:28 +00:00
2019-09-25 03:16:07 +00:00
export class FrameHandler {
2019-10-07 23:52:34 +00:00
public frameViewerMouseHeld: boolean;
2019-09-27 00:45:28 +00:00
private start: number = 0;
2019-09-26 01:59:16 +00:00
private frameNumberDiv: HTMLElement;
2019-09-27 23:45:52 +00:00
2019-09-27 00:45:28 +00:00
private animationData: IAnimationData;
private canvasData: IProjectData;
2019-09-26 01:59:16 +00:00
2019-09-25 03:16:07 +00:00
private filenames: string[] = [];
private currentFrame: number = 0;
2019-09-26 23:28:35 +00:00
private playingAnimation: boolean;
2019-09-25 03:16:07 +00:00
2019-09-27 23:45:52 +00:00
private htmlCanvasElement: HTMLCanvasElement;
2019-09-27 02:02:46 +00:00
private canvasContext: CanvasRenderingContext2D;
private imageElement: HTMLImageElement;
private projectData: IProjectData;
2019-10-04 01:02:08 +00:00
private frameViewer: HTMLElement;
2019-09-27 02:02:46 +00:00
constructor(
animationData: IAnimationData,
canvasData: IProjectData,
2019-09-27 23:45:52 +00:00
htmlCanvasElement: HTMLCanvasElement,
2019-09-27 02:02:46 +00:00
canvasContext: CanvasRenderingContext2D,
2019-09-27 23:45:52 +00:00
frameNumberDiv: HTMLElement,
imageElement: HTMLImageElement,
2019-10-04 01:02:08 +00:00
projectData: IProjectData,
frameViewer: HTMLElement
2019-09-27 02:02:46 +00:00
) {
2019-09-27 00:45:28 +00:00
this.animationData = animationData;
2019-09-27 23:45:52 +00:00
this.canvasData = canvasData;
this.htmlCanvasElement = htmlCanvasElement;
2019-09-27 02:02:46 +00:00
this.canvasContext = canvasContext;
2019-09-26 01:59:16 +00:00
this.frameNumberDiv = frameNumberDiv;
2019-09-27 00:45:28 +00:00
window.requestAnimationFrame(this.windowAnimationUpdate);
2019-09-27 23:45:52 +00:00
this.imageElement = imageElement;
this.projectData = projectData;
2019-10-04 01:02:08 +00:00
this.frameViewer = frameViewer;
2019-10-07 23:52:34 +00:00
this.frameViewer.addEventListener('mousedown', () => {
this.frameViewerMouseHeld = true;
});
this.frameViewer.addEventListener('mouseup', () => {
this.frameViewerMouseHeld = false;
});
2019-09-26 01:59:16 +00:00
}
public GetCurrentFrame(): number {
return this.currentFrame;
2019-09-25 03:16:07 +00:00
}
public loadFrames(filenames: string[]) {
this.filenames = filenames;
this.currentFrame = 0;
2019-09-27 02:02:46 +00:00
this.RefreshImage();
2019-09-25 03:16:07 +00:00
}
public AdvanceFrames(amount: number) {
this.currentFrame += amount;
2019-10-01 04:56:49 +00:00
if (this.animationData.loop || !this.playingAnimation) {
this.currentFrame %= this.filenames.length;
} else {
if (this.currentFrame > this.filenames.length - 1) {
this.playingAnimation = false;
this.currentFrame = Math.min(this.currentFrame, this.filenames.length - 1);
}
}
2019-09-25 03:16:07 +00:00
if (this.currentFrame < 0) {
this.currentFrame = this.filenames.length - 1;
}
this.GoToFrame(this.currentFrame);
}
public GoToFrame(frame: number) {
this.currentFrame = frame;
2019-09-27 02:02:46 +00:00
this.RefreshImage();
this.projectData.currentFrame = this.currentFrame;
2019-10-05 00:03:48 +00:00
this.RefreshFrameViewer();
2019-09-26 01:59:16 +00:00
}
2019-09-27 00:45:28 +00:00
public TogglePlayingAnimation() {
this.playingAnimation = !this.playingAnimation;
2019-10-01 04:58:04 +00:00
if (this.playingAnimation && this.currentFrame === this.filenames.length - 1 && !this.animationData.loop) {
2019-10-01 04:56:49 +00:00
this.currentFrame = -1;
}
2019-09-27 00:45:28 +00:00
}
public StopPlayingAnimation() {
this.playingAnimation = false;
}
2019-09-28 02:29:42 +00:00
public GetFilenames(): string[] {
return this.filenames;
}
2019-10-04 01:02:08 +00:00
public ConstructFrameUI = () => {
// clear frames
let child = this.frameViewer.lastElementChild;
while (child) {
this.frameViewer.removeChild(child);
child = this.frameViewer.lastElementChild;
}
// construct
for (let i = 0; i < this.animationData.frames.length; i++) {
const newDiv = document.createElement('div');
this.frameViewer.appendChild(newDiv);
newDiv.className = 'frame';
2019-10-04 01:11:26 +00:00
2019-10-07 23:52:34 +00:00
newDiv.addEventListener('mousedown', () => {
2019-10-04 01:11:26 +00:00
this.StopPlayingAnimation();
this.GoToFrame(i);
});
2019-10-07 23:52:34 +00:00
newDiv.addEventListener('mousemove', () => {
if (this.frameViewerMouseHeld) {
this.StopPlayingAnimation();
this.GoToFrame(i);
}
});
2019-10-04 01:02:08 +00:00
}
};
public RefreshFrameViewer() {
// set all frames to inactive
for (let i = 0; i < this.frameViewer.children.length; i++) {
2019-10-04 23:38:11 +00:00
this.frameViewer.children[i].classList.remove('selected', 'warning');
2019-10-04 01:02:08 +00:00
}
// set current frame to active
if (this.frameViewer.children[this.projectData.currentFrame] !== undefined) {
2019-10-04 23:38:11 +00:00
this.frameViewer.children[this.projectData.currentFrame].classList.add('selected');
2019-10-04 01:02:08 +00:00
}
// check frames for data errors
for (let f = 0; f < this.animationData.frames.length; f++) {
2019-10-04 23:38:11 +00:00
// this.frameViewer.children[f].classList.add('warning');
2019-10-04 01:02:08 +00:00
if (this.animationData.pins !== undefined) {
for (let p = 0; p < this.animationData.pins.length; p++) {
if (this.animationData.pins[p] !== undefined) {
const pinIDtoCheck = this.animationData.pins[p].id;
2019-10-04 23:38:11 +00:00
// console.log('checking frame ' + f + ' for pinID ' + this.animationData.pins[p].name);
2019-10-04 01:02:08 +00:00
if (this.frameViewer.children[f] !== undefined) {
2019-10-09 01:50:03 +00:00
if (this.animationData.frames[f].pinData[pinIDtoCheck] === undefined) {
2019-10-04 23:38:11 +00:00
this.frameViewer.children[f].classList.add('warning');
2019-10-04 01:02:08 +00:00
break;
}
}
}
}
}
}
}
2019-09-27 02:02:46 +00:00
private RefreshImage() {
2019-09-26 01:59:16 +00:00
if (this.filenames.length === 0) {
2019-10-04 23:38:11 +00:00
// this.frameNumberDiv.className = 'warning';
// this.frameNumberDiv.innerText = 'No images uploaded yet';
2019-09-26 01:59:16 +00:00
} else {
2019-09-27 23:45:52 +00:00
this.canvasContext.clearRect(0, 0, this.htmlCanvasElement.width, this.htmlCanvasElement.height);
2019-09-28 01:35:27 +00:00
this.canvasContext.imageSmoothingEnabled = false;
2019-09-27 02:02:46 +00:00
this.imageElement.src = this.filenames[this.currentFrame];
2019-09-27 23:45:52 +00:00
// draw sprite
this.canvasContext.drawImage(
this.imageElement,
0,
0,
this.htmlCanvasElement.width,
this.htmlCanvasElement.height
);
// draw origin +
this.canvasContext.strokeStyle = '#000000';
const originX = this.animationData.originX;
const originY = this.animationData.originY;
2019-10-04 01:02:08 +00:00
if (originX !== null && originY !== null) {
this.DrawCrossHair(500, this.canvasContext, originX, originY);
}
// frame number update
2019-10-04 01:02:08 +00:00
this.frameNumberDiv.className = '';
this.frameNumberDiv.innerText =
2019-10-08 22:15:49 +00:00
(this.currentFrame + 1).toString() + ' / ' + this.filenames.length.toString();
// draw pins
for (let i = 0; i < 10; i++) {
this.canvasContext.strokeStyle = '#FF0000';
if (this.animationData.frames[this.projectData.currentFrame] !== undefined) {
2019-10-09 01:50:03 +00:00
const currentSelectedPinData = this.animationData.frames[this.projectData.currentFrame].pinData[i];
if (currentSelectedPinData !== null && currentSelectedPinData !== undefined) {
this.DrawCrossHair(50, this.canvasContext, currentSelectedPinData.x, currentSelectedPinData.y);
}
}
}
2019-09-26 01:59:16 +00:00
}
2019-09-25 03:16:07 +00:00
}
2019-09-26 23:28:35 +00:00
private DrawCrossHair(size: number, canvasContext: CanvasRenderingContext2D, x: number, y: number) {
x *= this.canvasData.widthRatio;
y *= this.canvasData.heightRatio;
canvasContext.beginPath();
canvasContext.moveTo(x, y - size);
canvasContext.lineTo(x, y + size);
canvasContext.moveTo(x - size, y);
canvasContext.lineTo(x + size, y);
canvasContext.stroke();
}
2019-09-27 00:45:28 +00:00
private windowAnimationUpdate = (timestamp: number) => {
if (this.start === 0) {
this.start = timestamp;
}
const progress = timestamp - this.start;
if (this.playingAnimation && progress > 1000 / this.animationData.frameRate) {
this.AdvanceFrames(1);
this.start = 0;
}
2019-09-28 01:38:27 +00:00
this.RefreshImage();
2019-09-27 00:45:28 +00:00
window.requestAnimationFrame(this.windowAnimationUpdate);
2019-09-27 01:07:41 +00:00
// console.log('timestamp = ' + timestamp);
2019-09-27 00:45:28 +00:00
};
2019-09-25 03:16:07 +00:00
}