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; | ||||
| 			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
 | ||||
|  |  | |||
							
								
								
									
										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 (!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); | ||||
| 	}; | ||||
| } | ||||
|  |  | |||
|  | @ -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