diff --git a/app/Interfaces/IAnimationData.ts b/app/Interfaces/IAnimationData.ts index 89d9db4..fb29901 100644 --- a/app/Interfaces/IAnimationData.ts +++ b/app/Interfaces/IAnimationData.ts @@ -3,8 +3,8 @@ import { IPin } from './IPin'; export interface IAnimationData { frameRate: number; - originX: number; - originY: number; + originX: number | null; + originY: number | null; loop: boolean; frames: IFrame[]; pins: IPin[]; diff --git a/app/frame_handler.ts b/app/frame_handler.ts index aa5fc2e..907b2e1 100644 --- a/app/frame_handler.ts +++ b/app/frame_handler.ts @@ -20,6 +20,7 @@ export class FrameHandler { private imageElement: HTMLImageElement; private projectData: IProjectData; + private frameViewer: HTMLElement; constructor( animationData: IAnimationData, @@ -28,7 +29,8 @@ export class FrameHandler { canvasContext: CanvasRenderingContext2D, frameNumberDiv: HTMLElement, imageElement: HTMLImageElement, - projectData: IProjectData + projectData: IProjectData, + frameViewer: HTMLElement ) { this.animationData = animationData; this.canvasData = canvasData; @@ -38,6 +40,7 @@ export class FrameHandler { window.requestAnimationFrame(this.windowAnimationUpdate); this.imageElement = imageElement; this.projectData = projectData; + this.frameViewer = frameViewer; } public GetCurrentFrame(): number { @@ -64,6 +67,7 @@ export class FrameHandler { this.currentFrame = this.filenames.length - 1; } this.GoToFrame(this.currentFrame); + this.RefreshFrameViewer(); } public GoToFrame(frame: number) { @@ -87,6 +91,54 @@ export class FrameHandler { 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() { if (this.filenames.length === 0) { this.frameNumberDiv.className = 'warning'; @@ -105,12 +157,13 @@ export class FrameHandler { ); // draw origin + this.canvasContext.strokeStyle = '#000000'; - const originCursorSize: number = 500; const originX = this.animationData.originX; 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 - this.frameNumberDiv.className = 'instruction'; + this.frameNumberDiv.className = ''; this.frameNumberDiv.innerText = 'Frame ' + (this.currentFrame + 1).toString() + ' / ' + this.filenames.length.toString(); // draw pins diff --git a/app/page.ts b/app/page.ts index e2fce69..0407913 100644 --- a/app/page.ts +++ b/app/page.ts @@ -87,7 +87,8 @@ export class Page { canvasElement.getContext('2d')!, document.getElementById('frameNumber') as HTMLElement, imageElement, - this.projectData + this.projectData, + document.getElementById('frameViewer') as HTMLElement ); // input elements @@ -161,7 +162,7 @@ export class Page { if (document.activeElement === document.body) { this.pinHandler.UpdateAnimationPinNames(); - if (this.CheckAllFramesForPinData()) { + if (this.ProjectHasNeccesaryData()) { const zip = new JSZip(); // name of project const name = this.filenameInput.value; @@ -188,34 +189,47 @@ export class Page { document.addEventListener('keydown', keyDown); } - private CheckAllFramesForPinData(): boolean { - const availablePins: number[] = this.pinHandler.GetAvailablePins(); - let passTest: boolean = true; - let passPinDataTest: boolean = true; + private ProjectHasNeccesaryData(): boolean { + let pass: boolean = true; let errorString: string = ''; - let pinErrorString: string = ''; - for (let frame = 0; frame < this.animationData.frames.length; frame++) { - for (let p = 0; p < availablePins.length; p++) { - // loop through available pinIDs - const pinIDChecking = availablePins[p]; - if (this.animationData.frames[frame][pinIDChecking] === undefined) { - pinErrorString += 'Frame ' + frame + ', ' + this.pinHandler.GetPinName(pinIDChecking) + '\n'; - passPinDataTest = false; + this.frameHandler.RefreshFrameViewer(); + if (this.filenameInput.value === '') { + errorString += '- Missing name\n'; + pass = false; + } + if (this.animationData.originX === null || this.animationData.originY === null) { + errorString += '- Missing origin data\n'; + 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 (!passPinData) { + errorString += '- Missing pin data on some frames: \n' + pinDataErrorString; + pass = false; } - if (!passPinDataTest) { - // warn user if missing pin data - errorString += 'Missing the following pin data: \n\n' + pinErrorString; - passTest = false; + if (!pass) { + alert(errorString); } - this.message.innerText = errorString; - return passTest; + return pass; } private handleFileSelect = async (event: DragEvent) => { @@ -242,14 +256,14 @@ export class Page { this.canvasHandler.ResizeCanvas(); - // set framedata initialized to true + this.frameHandler.ConstructFrameUI(); }; private ResetProgram = () => { // defining blank slate animation data this.animationData.pins = []; - this.animationData.originX = -999; - this.animationData.originY = -999; + this.animationData.originX = null; + this.animationData.originY = null; this.animationData.frameRate = 30; this.animationData.loop = true; this.animationData.frames = [ { filename: '' } ]; @@ -282,9 +296,4 @@ export class Page { this.frameHandler.TogglePlayingAnimation(); console.log('new frame rate = ' + this.animationData.frameRate); }; - - private updateLooping = () => { - this.animationData.loop = this.loopingInput.checked; - console.log('new looping value = ' + this.loopingInput.checked); - }; } diff --git a/app/pin_handler.ts b/app/pin_handler.ts index c50176f..2f7dc25 100644 --- a/app/pin_handler.ts +++ b/app/pin_handler.ts @@ -43,6 +43,7 @@ export class PinHandler { for (let i = 1; i < this.allPinContainers.length; i++) { console.log(this.allPinContainers[i].children); const pinName: string = this.GetPinNameFromDiv(this.allPinContainers[i]); + console.log('new pin name = ' + pinName); if (pinName !== null && pinName !== undefined) { let newPinData: IPin = { id: this.GetPinNumberFromID(this.allPinContainers[i].id), @@ -111,6 +112,9 @@ export class PinHandler { newNameInput.id = 'nameInput_' + this.pins.toString(); newDiv.appendChild(newNameInput); newNameInput.value = 'PinName_' + this.pins.toString(); + newNameInput.addEventListener('focusout', () => { + this.UpdateAnimationPinNames(); + }); // button to remove pin const removePinButton = document.createElement('button'); newDiv.appendChild(removePinButton); @@ -133,6 +137,7 @@ export class PinHandler { this.RemovePinDataForID(idNumber); // remove the div itself newDiv.remove(); + this.UpdateAnimationPinNames(); }); // break newDiv.appendChild(document.createElement('br')); @@ -145,7 +150,9 @@ export class PinHandler { newDiv.className = 'pinButtonContainerSelected'; this.projectData.currentlySelectedPin = parseInt(newDiv.id.split('_')[1]); console.log('selected pin ' + this.projectData.currentlySelectedPin); + this.UpdateAnimationPinNames(); }); + this.UpdateAnimationPinNames(); }; private RemovePinDataForID = (pinID: number) => { diff --git a/dist/assets/stylesheets/main.css b/dist/assets/stylesheets/main.css index d62332e..67438b4 100644 --- a/dist/assets/stylesheets/main.css +++ b/dist/assets/stylesheets/main.css @@ -17,32 +17,53 @@ div { text-align: center; } +#frameViewer { + display: flex; + flex-direction: row; +} .frame { + flex: 1; width: 32px; height: 32px; + max-width: 32px; color: #101e24; - width: 50%; border: 2px solid #3f4446; padding: 1px; background-color: rgb(90, 92, 95); + display: inline-block; } .frameActive { + flex: 1; width: 32px; height: 32px; + max-width: 32px; color: #101e24; - width: 50%; border: 2px solid #0865df; padding: 1px; background-color: rgb(35, 75, 185); + display: inline-block; } .frameWarning { + flex: 1; width: 32px; height: 32px; + max-width: 32px; color: #101e24; - width: 50%; border: 2px solid rgb(233, 7, 75); padding: 1px; 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 { @@ -79,12 +100,14 @@ body { flex-direction: row; } .pinButtonContainer { + max-width: 10%; flex: 1; font-size: 12px; border: 2px solid #6b7578; padding: 1px; } .pinButtonContainerSelected { + max-width: 10%; flex: 1; font-size: 12px; border: 2px solid #0865df; diff --git a/dist/index.html b/dist/index.html index af37b07..c67f8c5 100644 --- a/dist/index.html +++ b/dist/index.html @@ -13,14 +13,12 @@

Drag images onto the page to upload them. Advance frames with arrow keys

- -
-

-
+
+

Origin