frame viewer, export warnings
parent
6c0cf01164
commit
2e47bde496
|
@ -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[];
|
||||
|
|
|
@ -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;
|
||||
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
|
||||
|
|
73
app/page.ts
73
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 (!passPinDataTest) {
|
||||
// warn user if missing pin data
|
||||
errorString += 'Missing the following pin data: \n\n' + pinErrorString;
|
||||
passTest = false;
|
||||
}
|
||||
this.message.innerText = errorString;
|
||||
return passTest;
|
||||
if (!passPinData) {
|
||||
errorString += '- Missing pin data on some frames: \n' + pinDataErrorString;
|
||||
pass = false;
|
||||
}
|
||||
if (!pass) {
|
||||
alert(errorString);
|
||||
}
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -13,14 +13,12 @@
|
|||
<p>Drag images onto the page to upload them. Advance frames with arrow keys</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div id="message" class="errorMessage"></div> -->
|
||||
<div id="frameNumber" class="warning">
|
||||
<p></p>
|
||||
</div>
|
||||
<!-- canvas -->
|
||||
<div id="canvasStyle">
|
||||
<canvas id="canvasImage" alt=""></canvas>
|
||||
</div>
|
||||
<div id="frameNumber" class="warning"></div>
|
||||
<div id="frameViewer"></div>
|
||||
<button id="addpin">Create New Pin</button>
|
||||
<div id="pinContainer" class="pinContainer">
|
||||
<div class="pinButtonContainerSelected" id="originPin"><p>Origin</p><button id="selectOrigin">Select</button>
|
||||
|
|
Loading…
Reference in New Issue