frame viewer, export warnings
parent
6c0cf01164
commit
2e47bde496
|
@ -3,8 +3,8 @@ import { IPin } from './IPin';
|
||||||
|
|
||||||
export interface IAnimationData {
|
export interface IAnimationData {
|
||||||
frameRate: number;
|
frameRate: number;
|
||||||
originX: number;
|
originX: number | null;
|
||||||
originY: number;
|
originY: number | null;
|
||||||
loop: boolean;
|
loop: boolean;
|
||||||
frames: IFrame[];
|
frames: IFrame[];
|
||||||
pins: IPin[];
|
pins: IPin[];
|
||||||
|
|
|
@ -20,6 +20,7 @@ export class FrameHandler {
|
||||||
private imageElement: HTMLImageElement;
|
private imageElement: HTMLImageElement;
|
||||||
|
|
||||||
private projectData: IProjectData;
|
private projectData: IProjectData;
|
||||||
|
private frameViewer: HTMLElement;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
animationData: IAnimationData,
|
animationData: IAnimationData,
|
||||||
|
@ -28,7 +29,8 @@ export class FrameHandler {
|
||||||
canvasContext: CanvasRenderingContext2D,
|
canvasContext: CanvasRenderingContext2D,
|
||||||
frameNumberDiv: HTMLElement,
|
frameNumberDiv: HTMLElement,
|
||||||
imageElement: HTMLImageElement,
|
imageElement: HTMLImageElement,
|
||||||
projectData: IProjectData
|
projectData: IProjectData,
|
||||||
|
frameViewer: HTMLElement
|
||||||
) {
|
) {
|
||||||
this.animationData = animationData;
|
this.animationData = animationData;
|
||||||
this.canvasData = canvasData;
|
this.canvasData = canvasData;
|
||||||
|
@ -38,6 +40,7 @@ export class FrameHandler {
|
||||||
window.requestAnimationFrame(this.windowAnimationUpdate);
|
window.requestAnimationFrame(this.windowAnimationUpdate);
|
||||||
this.imageElement = imageElement;
|
this.imageElement = imageElement;
|
||||||
this.projectData = projectData;
|
this.projectData = projectData;
|
||||||
|
this.frameViewer = frameViewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetCurrentFrame(): number {
|
public GetCurrentFrame(): number {
|
||||||
|
@ -64,6 +67,7 @@ export class FrameHandler {
|
||||||
this.currentFrame = this.filenames.length - 1;
|
this.currentFrame = this.filenames.length - 1;
|
||||||
}
|
}
|
||||||
this.GoToFrame(this.currentFrame);
|
this.GoToFrame(this.currentFrame);
|
||||||
|
this.RefreshFrameViewer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GoToFrame(frame: number) {
|
public GoToFrame(frame: number) {
|
||||||
|
@ -87,6 +91,54 @@ export class FrameHandler {
|
||||||
return this.filenames;
|
return this.filenames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public RefreshFrameViewer() {
|
||||||
|
// set all frames to inactive
|
||||||
|
for (let i = 0; i < this.frameViewer.children.length; i++) {
|
||||||
|
this.frameViewer.children[i].className = 'frame';
|
||||||
|
}
|
||||||
|
// set current frame to active
|
||||||
|
if (this.frameViewer.children[this.projectData.currentFrame] !== undefined) {
|
||||||
|
this.frameViewer.children[this.projectData.currentFrame].className = 'frameActive';
|
||||||
|
}
|
||||||
|
|
||||||
|
// check frames for data errors
|
||||||
|
for (let f = 0; f < this.animationData.frames.length; f++) {
|
||||||
|
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;
|
||||||
|
console.log('checking frame ' + f + ' for pinID ' + this.animationData.pins[p].name);
|
||||||
|
if (this.frameViewer.children[f] !== undefined) {
|
||||||
|
if (this.animationData.frames[f][pinIDtoCheck] === undefined) {
|
||||||
|
if (f === this.projectData.currentFrame) {
|
||||||
|
this.frameViewer.children[f].className = 'frameActiveWarning';
|
||||||
|
} else {
|
||||||
|
this.frameViewer.children[f].className = 'frameWarning';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private RefreshImage() {
|
private RefreshImage() {
|
||||||
if (this.filenames.length === 0) {
|
if (this.filenames.length === 0) {
|
||||||
this.frameNumberDiv.className = 'warning';
|
this.frameNumberDiv.className = 'warning';
|
||||||
|
@ -105,12 +157,13 @@ export class FrameHandler {
|
||||||
);
|
);
|
||||||
// draw origin +
|
// draw origin +
|
||||||
this.canvasContext.strokeStyle = '#000000';
|
this.canvasContext.strokeStyle = '#000000';
|
||||||
const originCursorSize: number = 500;
|
|
||||||
const originX = this.animationData.originX;
|
const originX = this.animationData.originX;
|
||||||
const originY = this.animationData.originY;
|
const originY = this.animationData.originY;
|
||||||
|
if (originX !== null && originY !== null) {
|
||||||
this.DrawCrossHair(500, this.canvasContext, originX, originY);
|
this.DrawCrossHair(500, this.canvasContext, originX, originY);
|
||||||
|
}
|
||||||
// frame number update
|
// frame number update
|
||||||
this.frameNumberDiv.className = 'instruction';
|
this.frameNumberDiv.className = '';
|
||||||
this.frameNumberDiv.innerText =
|
this.frameNumberDiv.innerText =
|
||||||
'Frame ' + (this.currentFrame + 1).toString() + ' / ' + this.filenames.length.toString();
|
'Frame ' + (this.currentFrame + 1).toString() + ' / ' + this.filenames.length.toString();
|
||||||
// draw pins
|
// draw pins
|
||||||
|
|
73
app/page.ts
73
app/page.ts
|
@ -87,7 +87,8 @@ export class Page {
|
||||||
canvasElement.getContext('2d')!,
|
canvasElement.getContext('2d')!,
|
||||||
document.getElementById('frameNumber') as HTMLElement,
|
document.getElementById('frameNumber') as HTMLElement,
|
||||||
imageElement,
|
imageElement,
|
||||||
this.projectData
|
this.projectData,
|
||||||
|
document.getElementById('frameViewer') as HTMLElement
|
||||||
);
|
);
|
||||||
|
|
||||||
// input elements
|
// input elements
|
||||||
|
@ -161,7 +162,7 @@ export class Page {
|
||||||
if (document.activeElement === document.body) {
|
if (document.activeElement === document.body) {
|
||||||
this.pinHandler.UpdateAnimationPinNames();
|
this.pinHandler.UpdateAnimationPinNames();
|
||||||
|
|
||||||
if (this.CheckAllFramesForPinData()) {
|
if (this.ProjectHasNeccesaryData()) {
|
||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
// name of project
|
// name of project
|
||||||
const name = this.filenameInput.value;
|
const name = this.filenameInput.value;
|
||||||
|
@ -188,34 +189,47 @@ export class Page {
|
||||||
document.addEventListener('keydown', keyDown);
|
document.addEventListener('keydown', keyDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CheckAllFramesForPinData(): boolean {
|
private ProjectHasNeccesaryData(): boolean {
|
||||||
const availablePins: number[] = this.pinHandler.GetAvailablePins();
|
let pass: boolean = true;
|
||||||
let passTest: boolean = true;
|
|
||||||
let passPinDataTest: boolean = true;
|
|
||||||
let errorString: string = '';
|
let errorString: string = '';
|
||||||
let pinErrorString: string = '';
|
this.frameHandler.RefreshFrameViewer();
|
||||||
for (let frame = 0; frame < this.animationData.frames.length; frame++) {
|
if (this.filenameInput.value === '') {
|
||||||
for (let p = 0; p < availablePins.length; p++) {
|
errorString += '- Missing name\n';
|
||||||
// loop through available pinIDs
|
pass = false;
|
||||||
const pinIDChecking = availablePins[p];
|
}
|
||||||
if (this.animationData.frames[frame][pinIDChecking] === undefined) {
|
if (this.animationData.originX === null || this.animationData.originY === null) {
|
||||||
pinErrorString += 'Frame ' + frame + ', ' + this.pinHandler.GetPinName(pinIDChecking) + '\n';
|
errorString += '- Missing origin data\n';
|
||||||
passPinDataTest = false;
|
pass = false;
|
||||||
|
}
|
||||||
|
// check frames for data errors
|
||||||
|
let pinDataErrorString: string = '';
|
||||||
|
let passPinData: boolean = true;
|
||||||
|
for (let f = 0; f < this.animationData.frames.length; f++) {
|
||||||
|
let errorOnFrame: boolean = false;
|
||||||
|
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;
|
||||||
|
console.log('checking frame ' + f + ' for pinID ' + this.animationData.pins[p].name);
|
||||||
|
if (this.animationData.frames[f][pinIDtoCheck] === undefined) {
|
||||||
|
if (!errorOnFrame) {
|
||||||
|
pinDataErrorString += ' Frame ' + f + ' :\n';
|
||||||
|
}
|
||||||
|
pinDataErrorString += ' Pin: ' + this.animationData.pins[p].name + '\n';
|
||||||
|
passPinData = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// construct error string
|
|
||||||
if (this.animationData.originX === -999 || this.animationData.originY === -999) {
|
|
||||||
errorString = 'Missing Origin data. \n';
|
|
||||||
passTest = false;
|
|
||||||
}
|
}
|
||||||
if (!passPinDataTest) {
|
|
||||||
// warn user if missing pin data
|
|
||||||
errorString += 'Missing the following pin data: \n\n' + pinErrorString;
|
|
||||||
passTest = false;
|
|
||||||
}
|
}
|
||||||
this.message.innerText = errorString;
|
if (!passPinData) {
|
||||||
return passTest;
|
errorString += '- Missing pin data on some frames: \n' + pinDataErrorString;
|
||||||
|
pass = false;
|
||||||
|
}
|
||||||
|
if (!pass) {
|
||||||
|
alert(errorString);
|
||||||
|
}
|
||||||
|
return pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleFileSelect = async (event: DragEvent) => {
|
private handleFileSelect = async (event: DragEvent) => {
|
||||||
|
@ -242,14 +256,14 @@ export class Page {
|
||||||
|
|
||||||
this.canvasHandler.ResizeCanvas();
|
this.canvasHandler.ResizeCanvas();
|
||||||
|
|
||||||
// set framedata initialized to true
|
this.frameHandler.ConstructFrameUI();
|
||||||
};
|
};
|
||||||
|
|
||||||
private ResetProgram = () => {
|
private ResetProgram = () => {
|
||||||
// defining blank slate animation data
|
// defining blank slate animation data
|
||||||
this.animationData.pins = [];
|
this.animationData.pins = [];
|
||||||
this.animationData.originX = -999;
|
this.animationData.originX = null;
|
||||||
this.animationData.originY = -999;
|
this.animationData.originY = null;
|
||||||
this.animationData.frameRate = 30;
|
this.animationData.frameRate = 30;
|
||||||
this.animationData.loop = true;
|
this.animationData.loop = true;
|
||||||
this.animationData.frames = [ { filename: '' } ];
|
this.animationData.frames = [ { filename: '' } ];
|
||||||
|
@ -282,9 +296,4 @@ export class Page {
|
||||||
this.frameHandler.TogglePlayingAnimation();
|
this.frameHandler.TogglePlayingAnimation();
|
||||||
console.log('new frame rate = ' + this.animationData.frameRate);
|
console.log('new frame rate = ' + this.animationData.frameRate);
|
||||||
};
|
};
|
||||||
|
|
||||||
private updateLooping = () => {
|
|
||||||
this.animationData.loop = this.loopingInput.checked;
|
|
||||||
console.log('new looping value = ' + this.loopingInput.checked);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ export class PinHandler {
|
||||||
for (let i = 1; i < this.allPinContainers.length; i++) {
|
for (let i = 1; i < this.allPinContainers.length; i++) {
|
||||||
console.log(this.allPinContainers[i].children);
|
console.log(this.allPinContainers[i].children);
|
||||||
const pinName: string = this.GetPinNameFromDiv(this.allPinContainers[i]);
|
const pinName: string = this.GetPinNameFromDiv(this.allPinContainers[i]);
|
||||||
|
console.log('new pin name = ' + pinName);
|
||||||
if (pinName !== null && pinName !== undefined) {
|
if (pinName !== null && pinName !== undefined) {
|
||||||
let newPinData: IPin = {
|
let newPinData: IPin = {
|
||||||
id: this.GetPinNumberFromID(this.allPinContainers[i].id),
|
id: this.GetPinNumberFromID(this.allPinContainers[i].id),
|
||||||
|
@ -111,6 +112,9 @@ export class PinHandler {
|
||||||
newNameInput.id = 'nameInput_' + this.pins.toString();
|
newNameInput.id = 'nameInput_' + this.pins.toString();
|
||||||
newDiv.appendChild(newNameInput);
|
newDiv.appendChild(newNameInput);
|
||||||
newNameInput.value = 'PinName_' + this.pins.toString();
|
newNameInput.value = 'PinName_' + this.pins.toString();
|
||||||
|
newNameInput.addEventListener('focusout', () => {
|
||||||
|
this.UpdateAnimationPinNames();
|
||||||
|
});
|
||||||
// button to remove pin
|
// button to remove pin
|
||||||
const removePinButton = document.createElement('button');
|
const removePinButton = document.createElement('button');
|
||||||
newDiv.appendChild(removePinButton);
|
newDiv.appendChild(removePinButton);
|
||||||
|
@ -133,6 +137,7 @@ export class PinHandler {
|
||||||
this.RemovePinDataForID(idNumber);
|
this.RemovePinDataForID(idNumber);
|
||||||
// remove the div itself
|
// remove the div itself
|
||||||
newDiv.remove();
|
newDiv.remove();
|
||||||
|
this.UpdateAnimationPinNames();
|
||||||
});
|
});
|
||||||
// break
|
// break
|
||||||
newDiv.appendChild(document.createElement('br'));
|
newDiv.appendChild(document.createElement('br'));
|
||||||
|
@ -145,7 +150,9 @@ export class PinHandler {
|
||||||
newDiv.className = 'pinButtonContainerSelected';
|
newDiv.className = 'pinButtonContainerSelected';
|
||||||
this.projectData.currentlySelectedPin = parseInt(newDiv.id.split('_')[1]);
|
this.projectData.currentlySelectedPin = parseInt(newDiv.id.split('_')[1]);
|
||||||
console.log('selected pin ' + this.projectData.currentlySelectedPin);
|
console.log('selected pin ' + this.projectData.currentlySelectedPin);
|
||||||
|
this.UpdateAnimationPinNames();
|
||||||
});
|
});
|
||||||
|
this.UpdateAnimationPinNames();
|
||||||
};
|
};
|
||||||
|
|
||||||
private RemovePinDataForID = (pinID: number) => {
|
private RemovePinDataForID = (pinID: number) => {
|
||||||
|
|
|
@ -17,32 +17,53 @@ div {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#frameViewer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
.frame {
|
.frame {
|
||||||
|
flex: 1;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
max-width: 32px;
|
||||||
color: #101e24;
|
color: #101e24;
|
||||||
width: 50%;
|
|
||||||
border: 2px solid #3f4446;
|
border: 2px solid #3f4446;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
background-color: rgb(90, 92, 95);
|
background-color: rgb(90, 92, 95);
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
.frameActive {
|
.frameActive {
|
||||||
|
flex: 1;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
max-width: 32px;
|
||||||
color: #101e24;
|
color: #101e24;
|
||||||
width: 50%;
|
|
||||||
border: 2px solid #0865df;
|
border: 2px solid #0865df;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
background-color: rgb(35, 75, 185);
|
background-color: rgb(35, 75, 185);
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
.frameWarning {
|
.frameWarning {
|
||||||
|
flex: 1;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
max-width: 32px;
|
||||||
color: #101e24;
|
color: #101e24;
|
||||||
width: 50%;
|
|
||||||
border: 2px solid rgb(233, 7, 75);
|
border: 2px solid rgb(233, 7, 75);
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
background-color: rgb(83, 14, 20);
|
background-color: rgb(83, 14, 20);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.frameActiveWarning {
|
||||||
|
flex: 1;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
max-width: 32px;
|
||||||
|
color: #101e24;
|
||||||
|
border: 2px solid rgb(233, 7, 75);
|
||||||
|
padding: 1px;
|
||||||
|
background-color: rgb(185, 8, 61);
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.errorMessage {
|
.errorMessage {
|
||||||
|
@ -79,12 +100,14 @@ body {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
.pinButtonContainer {
|
.pinButtonContainer {
|
||||||
|
max-width: 10%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
border: 2px solid #6b7578;
|
border: 2px solid #6b7578;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
}
|
}
|
||||||
.pinButtonContainerSelected {
|
.pinButtonContainerSelected {
|
||||||
|
max-width: 10%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
border: 2px solid #0865df;
|
border: 2px solid #0865df;
|
||||||
|
|
|
@ -13,14 +13,12 @@
|
||||||
<p>Drag images onto the page to upload them. Advance frames with arrow keys</p>
|
<p>Drag images onto the page to upload them. Advance frames with arrow keys</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div id="message" class="errorMessage"></div> -->
|
|
||||||
<div id="frameNumber" class="warning">
|
|
||||||
<p></p>
|
|
||||||
</div>
|
|
||||||
<!-- canvas -->
|
<!-- canvas -->
|
||||||
<div id="canvasStyle">
|
<div id="canvasStyle">
|
||||||
<canvas id="canvasImage" alt=""></canvas>
|
<canvas id="canvasImage" alt=""></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="frameNumber" class="warning"></div>
|
||||||
|
<div id="frameViewer"></div>
|
||||||
<button id="addpin">Create New Pin</button>
|
<button id="addpin">Create New Pin</button>
|
||||||
<div id="pinContainer" class="pinContainer">
|
<div id="pinContainer" class="pinContainer">
|
||||||
<div class="pinButtonContainerSelected" id="originPin"><p>Origin</p><button id="selectOrigin">Select</button>
|
<div class="pinButtonContainerSelected" id="originPin"><p>Origin</p><button id="selectOrigin">Select</button>
|
||||||
|
|
Loading…
Reference in New Issue