frame viewer, export warnings

master
Beau Blyth 2019-10-03 18:02:08 -07:00
parent 6c0cf01164
commit 2e47bde496
6 changed files with 135 additions and 45 deletions

View File

@ -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[];

View File

@ -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;
this.DrawCrossHair(500, this.canvasContext, originX, originY); if (originX !== null && originY !== null) {
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

View File

@ -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 (!passPinData) {
if (this.animationData.originX === -999 || this.animationData.originY === -999) { errorString += '- Missing pin data on some frames: \n' + pinDataErrorString;
errorString = 'Missing Origin data. \n'; pass = false;
passTest = false;
} }
if (!passPinDataTest) { if (!pass) {
// warn user if missing pin data alert(errorString);
errorString += 'Missing the following pin data: \n\n' + pinErrorString;
passTest = false;
} }
this.message.innerText = errorString; return pass;
return passTest;
} }
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);
};
} }

View File

@ -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) => {

View File

@ -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;

6
dist/index.html vendored
View File

@ -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>