adding removing pins, pin name reference
parent
bf0e66c77c
commit
def4998ec4
|
@ -1,6 +0,0 @@
|
|||
export interface ICanvasData {
|
||||
width: number;
|
||||
height: number;
|
||||
widthRatio: number;
|
||||
heightRatio: number;
|
||||
}
|
|
@ -2,5 +2,5 @@ import { IFramePinData } from './IFramePinData';
|
|||
|
||||
export interface IFrame {
|
||||
filename: string;
|
||||
pinData: IFramePinData[];
|
||||
[index: number]: IFramePinData;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
export interface IProjectData {
|
||||
currentFrame: number;
|
||||
currentlySelectedPin: number;
|
||||
width: number;
|
||||
height: number;
|
||||
widthRatio: number;
|
||||
heightRatio: number;
|
||||
}
|
|
@ -1,25 +1,26 @@
|
|||
import { IAnimationData } from './Interfaces/IAnimationData';
|
||||
import { ICanvasData } from './Interfaces/ICanvasData';
|
||||
import { IProjectData } from './Interfaces/IProjectData';
|
||||
import { IFramePinData } from './Interfaces/IFramePinData';
|
||||
|
||||
// I display the canvas and am clickable
|
||||
export class CanvasHandler {
|
||||
private canvasImage: HTMLCanvasElement;
|
||||
private imageElement: HTMLImageElement;
|
||||
private animationData: IAnimationData;
|
||||
private canvasData: ICanvasData;
|
||||
private projectData: IProjectData;
|
||||
private orginInfo: HTMLElement;
|
||||
|
||||
private targetImageSize: number = 256;
|
||||
|
||||
constructor(
|
||||
animationData: IAnimationData,
|
||||
canvasData: ICanvasData,
|
||||
canvasData: IProjectData,
|
||||
canvasImage: HTMLCanvasElement,
|
||||
imageElement: HTMLImageElement,
|
||||
originInfo: HTMLElement
|
||||
) {
|
||||
this.animationData = animationData;
|
||||
this.canvasData = canvasData;
|
||||
this.projectData = canvasData;
|
||||
this.canvasImage = canvasImage;
|
||||
this.imageElement = imageElement;
|
||||
this.orginInfo = originInfo;
|
||||
|
@ -45,8 +46,8 @@ export class CanvasHandler {
|
|||
}
|
||||
|
||||
private UpdateCanvasDataSize() {
|
||||
this.canvasData.width = this.canvasImage.width;
|
||||
this.canvasData.height = this.canvasImage.height;
|
||||
this.projectData.width = this.canvasImage.width;
|
||||
this.projectData.height = this.canvasImage.height;
|
||||
}
|
||||
|
||||
private mouseDown = (event: MouseEvent) => {
|
||||
|
@ -57,12 +58,25 @@ export class CanvasHandler {
|
|||
const pixelX: number = Math.floor(event.offsetX / ratioWidth);
|
||||
const pixelY: number = Math.floor(event.offsetY / ratioHeight);
|
||||
console.log('CLICK X:' + pixelX + ' Y:' + pixelY);
|
||||
if (this.projectData.currentlySelectedPin === 0) {
|
||||
// update animation data
|
||||
this.animationData.originX = pixelX;
|
||||
this.animationData.originY = pixelY;
|
||||
} else {
|
||||
console.log('current pin id = ' + this.projectData.currentlySelectedPin);
|
||||
const newPinData: IFramePinData = {
|
||||
id: this.projectData.currentlySelectedPin,
|
||||
x: pixelX,
|
||||
y: pixelY
|
||||
};
|
||||
|
||||
this.animationData.frames[this.projectData.currentFrame][
|
||||
this.projectData.currentlySelectedPin
|
||||
] = newPinData;
|
||||
}
|
||||
// update canvas data
|
||||
this.canvasData.widthRatio = ratioWidth;
|
||||
this.canvasData.heightRatio = ratioHeight;
|
||||
this.projectData.widthRatio = ratioWidth;
|
||||
this.projectData.heightRatio = ratioHeight;
|
||||
// update origin number display
|
||||
this.orginInfo.innerText = 'Origin X: ' + this.animationData.originX + ' Y: ' + this.animationData.originY;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { IAnimationData } from './Interfaces/IAnimationData';
|
||||
import { ICanvasData } from './Interfaces/ICanvasData';
|
||||
import { IProjectData } from './Interfaces/IProjectData';
|
||||
import { IFramePinData } from './Interfaces/IFramePinData';
|
||||
|
||||
export class FrameHandler {
|
||||
private start: number = 0;
|
||||
|
@ -7,7 +8,7 @@ export class FrameHandler {
|
|||
private frameNumberDiv: HTMLElement;
|
||||
|
||||
private animationData: IAnimationData;
|
||||
private canvasData: ICanvasData;
|
||||
private canvasData: IProjectData;
|
||||
|
||||
private filenames: string[] = [];
|
||||
private currentFrame: number = 0;
|
||||
|
@ -18,13 +19,16 @@ export class FrameHandler {
|
|||
|
||||
private imageElement: HTMLImageElement;
|
||||
|
||||
private projectData: IProjectData;
|
||||
|
||||
constructor(
|
||||
animationData: IAnimationData,
|
||||
canvasData: ICanvasData,
|
||||
canvasData: IProjectData,
|
||||
htmlCanvasElement: HTMLCanvasElement,
|
||||
canvasContext: CanvasRenderingContext2D,
|
||||
frameNumberDiv: HTMLElement,
|
||||
imageElement: HTMLImageElement
|
||||
imageElement: HTMLImageElement,
|
||||
projectData: IProjectData
|
||||
) {
|
||||
this.animationData = animationData;
|
||||
this.canvasData = canvasData;
|
||||
|
@ -33,6 +37,7 @@ export class FrameHandler {
|
|||
this.frameNumberDiv = frameNumberDiv;
|
||||
window.requestAnimationFrame(this.windowAnimationUpdate);
|
||||
this.imageElement = imageElement;
|
||||
this.projectData = projectData;
|
||||
}
|
||||
|
||||
public GetCurrentFrame(): number {
|
||||
|
@ -57,6 +62,7 @@ export class FrameHandler {
|
|||
public GoToFrame(frame: number) {
|
||||
this.currentFrame = frame;
|
||||
this.RefreshImage();
|
||||
this.projectData.currentFrame = this.currentFrame;
|
||||
}
|
||||
|
||||
public TogglePlayingAnimation() {
|
||||
|
@ -88,20 +94,36 @@ export class FrameHandler {
|
|||
this.htmlCanvasElement.height
|
||||
);
|
||||
// draw origin +
|
||||
this.canvasContext.strokeStyle = '#000000';
|
||||
const originCursorSize: number = 500;
|
||||
const originX = this.animationData.originX * this.canvasData.widthRatio;
|
||||
const originY = this.animationData.originY * this.canvasData.heightRatio;
|
||||
this.canvasContext.beginPath();
|
||||
this.canvasContext.moveTo(originX, originY - originCursorSize);
|
||||
this.canvasContext.lineTo(originX, originY + originCursorSize);
|
||||
this.canvasContext.moveTo(originX - originCursorSize, originY);
|
||||
this.canvasContext.lineTo(originX + originCursorSize, originY);
|
||||
this.canvasContext.stroke();
|
||||
const originX = this.animationData.originX;
|
||||
const originY = this.animationData.originY;
|
||||
this.DrawCrossHair(500, this.canvasContext, originX, originY);
|
||||
// frame number update
|
||||
this.frameNumberDiv.className = 'instruction';
|
||||
this.frameNumberDiv.innerText =
|
||||
'Frame ' + (this.currentFrame + 1).toString() + ' / ' + this.filenames.length.toString();
|
||||
// draw pins
|
||||
for (let i = 0; i < 10; i++) {
|
||||
this.canvasContext.strokeStyle = '#FF0000';
|
||||
let currentSelectedPinData: IFramePinData = this.animationData.frames[this.projectData.currentFrame][i];
|
||||
if (currentSelectedPinData !== null && currentSelectedPinData !== undefined) {
|
||||
this.DrawCrossHair(50, this.canvasContext, currentSelectedPinData.x, currentSelectedPinData.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DrawCrossHair(size: number, canvasContext: CanvasRenderingContext2D, x: number, y: number) {
|
||||
x *= this.canvasData.widthRatio;
|
||||
y *= this.canvasData.heightRatio;
|
||||
canvasContext.beginPath();
|
||||
canvasContext.moveTo(x, y - size);
|
||||
canvasContext.lineTo(x, y + size);
|
||||
canvasContext.moveTo(x - size, y);
|
||||
canvasContext.lineTo(x + size, y);
|
||||
canvasContext.stroke();
|
||||
}
|
||||
|
||||
private windowAnimationUpdate = (timestamp: number) => {
|
||||
if (this.start === 0) {
|
||||
|
|
29
app/page.ts
29
app/page.ts
|
@ -4,7 +4,7 @@ import { CanvasHandler } from './canvas_handler';
|
|||
import { FileHandler } from './file_handler';
|
||||
import { FrameHandler } from './frame_handler';
|
||||
import { IAnimationData } from './Interfaces/IAnimationData';
|
||||
import { ICanvasData } from './Interfaces/ICanvasData';
|
||||
import { IProjectData } from './Interfaces/IProjectData';
|
||||
import { IFrame } from './Interfaces/IFrame';
|
||||
import { PinHandler } from './pin_handler';
|
||||
|
||||
|
@ -27,7 +27,7 @@ export class Page {
|
|||
private canvasImage: HTMLCanvasElement;
|
||||
private canvasContext: CanvasRenderingContext2DSettings;
|
||||
|
||||
private canvasData: ICanvasData;
|
||||
private projectData: IProjectData;
|
||||
private filenameInput: HTMLInputElement;
|
||||
|
||||
public Load() {
|
||||
|
@ -40,13 +40,14 @@ export class Page {
|
|||
loop: true,
|
||||
frames: [
|
||||
{
|
||||
filename: '',
|
||||
pinData: []
|
||||
filename: ''
|
||||
}
|
||||
]
|
||||
};
|
||||
// blank slate canvas data
|
||||
this.canvasData = {
|
||||
this.projectData = {
|
||||
currentFrame: 0,
|
||||
currentlySelectedPin: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
widthRatio: 0,
|
||||
|
@ -59,13 +60,17 @@ export class Page {
|
|||
|
||||
this.pinHandler = new PinHandler(
|
||||
document.getElementById('addpin') as HTMLElement,
|
||||
document.getElementById('pinSettings') as HTMLElement
|
||||
document.getElementById('pinSettings') as HTMLElement,
|
||||
document.getElementById('pinContainer') as HTMLElement,
|
||||
document.getElementById('originPin') as HTMLElement,
|
||||
this.projectData,
|
||||
this.animationData
|
||||
);
|
||||
|
||||
// setup canvas
|
||||
this.canvasHandler = new CanvasHandler(
|
||||
this.animationData,
|
||||
this.canvasData,
|
||||
this.projectData,
|
||||
canvasElement,
|
||||
imageElement,
|
||||
document.getElementById('originInfo') as HTMLElement
|
||||
|
@ -74,11 +79,12 @@ export class Page {
|
|||
// setup frame handler
|
||||
this.frameHandler = new FrameHandler(
|
||||
this.animationData,
|
||||
this.canvasData,
|
||||
this.projectData,
|
||||
canvasElement,
|
||||
canvasElement.getContext('2d')!,
|
||||
document.getElementById('frameNumber') as HTMLElement,
|
||||
imageElement
|
||||
imageElement,
|
||||
this.projectData
|
||||
);
|
||||
|
||||
// input elements
|
||||
|
@ -148,6 +154,8 @@ export class Page {
|
|||
}
|
||||
|
||||
case 83: {
|
||||
this.pinHandler.UpdateAnimationPinNames();
|
||||
|
||||
const zip = new JSZip();
|
||||
// name of project
|
||||
const name = this.filenameInput.value;
|
||||
|
@ -183,8 +191,7 @@ export class Page {
|
|||
|
||||
for (let i = 0; i < event.dataTransfer!.files.length; i++) {
|
||||
newFrames.push({
|
||||
filename: event.dataTransfer!.files[i].name,
|
||||
pinData: []
|
||||
filename: event.dataTransfer!.files[i].name
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,26 +1,103 @@
|
|||
import { IProjectData } from './Interfaces/IProjectData';
|
||||
import { IPin } from './Interfaces/IPin';
|
||||
import { IAnimationData } from './Interfaces/IAnimationData';
|
||||
|
||||
export class PinHandler {
|
||||
private addPinButton: HTMLElement;
|
||||
private pinSettingsDiv: HTMLElement;
|
||||
private pins: number = 1;
|
||||
private pinContainer: HTMLElement;
|
||||
private allPinContainers: HTMLElement[];
|
||||
private projectData: IProjectData;
|
||||
private animationData: IAnimationData;
|
||||
|
||||
constructor(addPinButton: HTMLElement, pinSettingsDiv: HTMLElement) {
|
||||
constructor(
|
||||
addPinButton: HTMLElement,
|
||||
pinSettingsDiv: HTMLElement,
|
||||
pinContainer: HTMLElement,
|
||||
originPin: HTMLElement,
|
||||
projectData: IProjectData,
|
||||
animationData: IAnimationData
|
||||
) {
|
||||
this.addPinButton = addPinButton;
|
||||
this.pinSettingsDiv = pinSettingsDiv;
|
||||
this.UpdatePinSettingsDiv();
|
||||
this.pinContainer = pinContainer;
|
||||
this.projectData = projectData;
|
||||
this.animationData = animationData;
|
||||
|
||||
// add origin click behaviour
|
||||
originPin.addEventListener('click', () => {
|
||||
this.DeselectAllPinContainers();
|
||||
originPin.className = 'pinButtonContainerSelected';
|
||||
projectData.currentlySelectedPin = 0;
|
||||
});
|
||||
// put origin into pincontainer array
|
||||
this.allPinContainers = [ originPin ];
|
||||
|
||||
// this.UpdatePinSettingsDiv();
|
||||
this.addPinButton.addEventListener('click', this.AddPinButtonPressed);
|
||||
}
|
||||
|
||||
private UpdatePinSettingsDiv() {
|
||||
let html: string = '';
|
||||
for (let i = 0; i < this.pins; i++) {
|
||||
html += '<input type="text" id="pinname' + i + '" value="pinname' + i + '">';
|
||||
public UpdateAnimationPinNames = () => {
|
||||
const animationPinData: IPin[] = [];
|
||||
for (let i = 1; i < this.allPinContainers.length; i++) {
|
||||
console.log(this.allPinContainers[i].children);
|
||||
let pinName: string = this.allPinContainers[i].getElementsByTagName('input')[0].value;
|
||||
if (pinName !== null && pinName !== undefined) {
|
||||
let newPinData: IPin = {
|
||||
id: parseInt(this.allPinContainers[i].id.split('_')[1]),
|
||||
name: pinName
|
||||
};
|
||||
animationPinData.push(newPinData);
|
||||
}
|
||||
this.pinSettingsDiv.innerHTML = html;
|
||||
}
|
||||
this.animationData.pins = animationPinData;
|
||||
console.log('updated animationPinData to ' + animationPinData);
|
||||
};
|
||||
|
||||
private AddPinButtonPressed() {
|
||||
this.pins += 1;
|
||||
private UpdatePinSettingsDiv = () => {
|
||||
// create info window div and append to pincontainer
|
||||
const newDiv = document.createElement('div');
|
||||
this.allPinContainers.push(newDiv);
|
||||
|
||||
this.pinContainer.appendChild(newDiv);
|
||||
newDiv.id = 'pinID_' + this.pins.toString();
|
||||
newDiv.className = 'pinButtonContainer';
|
||||
// text input field for pin name
|
||||
const newNameInput = document.createElement('input');
|
||||
newNameInput.id = 'nameInput_' + this.pins.toString();
|
||||
newDiv.appendChild(newNameInput);
|
||||
newNameInput.value = 'PinName_' + this.pins.toString();
|
||||
// button to remove pin
|
||||
const removePinButton = document.createElement('button');
|
||||
newDiv.appendChild(removePinButton);
|
||||
removePinButton.textContent = 'X';
|
||||
removePinButton.className = 'removeButton';
|
||||
removePinButton.addEventListener('click', () => {
|
||||
newDiv.remove();
|
||||
});
|
||||
// break
|
||||
newDiv.appendChild(document.createElement('br'));
|
||||
// select pin to place
|
||||
const selectPinButton = document.createElement('button');
|
||||
newDiv.appendChild(selectPinButton);
|
||||
selectPinButton.textContent = 'Select';
|
||||
selectPinButton.addEventListener('click', () => {
|
||||
this.DeselectAllPinContainers();
|
||||
newDiv.className = 'pinButtonContainerSelected';
|
||||
this.projectData.currentlySelectedPin = parseInt(newDiv.id.split('_')[1]);
|
||||
console.log('selected pin ' + this.projectData.currentlySelectedPin);
|
||||
});
|
||||
};
|
||||
|
||||
private DeselectAllPinContainers = () => {
|
||||
for (let i = 0; i < this.allPinContainers.length; i++) {
|
||||
this.allPinContainers[i].className = 'pinButtonContainer';
|
||||
}
|
||||
};
|
||||
|
||||
private AddPinButtonPressed = () => {
|
||||
this.UpdatePinSettingsDiv();
|
||||
}
|
||||
this.pins += 1;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -42,3 +42,26 @@ body {
|
|||
border: 2px solid #6b7578;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
.pinContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.pinButtonContainer {
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
border: 2px solid #6b7578;
|
||||
padding: 1px;
|
||||
}
|
||||
.pinButtonContainerSelected {
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
border: 2px solid #0865df;
|
||||
padding: 1px;
|
||||
background-color: rgb(35, 75, 185);
|
||||
}
|
||||
|
||||
.removeButton {
|
||||
background-color: rgb(158, 15, 34);
|
||||
color: rgb(227, 227, 236);
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -22,7 +22,10 @@
|
|||
<canvas id="canvasImage" alt=""></canvas>
|
||||
</div>
|
||||
<button id="addpin">Create New Pin</button>
|
||||
<div id="pinSettings"></div>
|
||||
<div id="pinContainer" class="pinContainer">
|
||||
<div class="pinButtonContainerSelected" id="originPin"><p>Origin</p><button id="selectOrigin">Select</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="settings">
|
||||
Name: <input type = "text" id="filename"><br>
|
||||
<div id = "originInfo">Click image to set Origin</div>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"0 debug pnpm:scope": {
|
||||
"selected": 1,
|
||||
"workspacePrefix": null
|
||||
},
|
||||
"1 error pnpm": {
|
||||
"message": {
|
||||
"errno": 1,
|
||||
"code": "ELIFECYCLE",
|
||||
"pkgid": "animationtool@1.0.0",
|
||||
"stage": "start:dev",
|
||||
"script": "webpack-dev-server --config webpack/dev.config.js",
|
||||
"pkgname": "animationtool"
|
||||
},
|
||||
"err": {
|
||||
"name": "Error",
|
||||
"message": "animationtool@1.0.0 start:dev: `webpack-dev-server --config webpack/dev.config.js`\nExit status 1",
|
||||
"code": "ELIFECYCLE",
|
||||
"stack": "Error: animationtool@1.0.0 start:dev: `webpack-dev-server --config webpack/dev.config.js`\nExit status 1\n at EventEmitter.proc.on (C:\\Users\\tekno\\AppData\\Roaming\\npm\\node_modules\\pnpm\\lib\\node_modules\\@zkochan\\npm-lifecycle\\index.js:302:16)\n at EventEmitter.emit (events.js:198:13)\n at ChildProcess.<anonymous> (C:\\Users\\tekno\\AppData\\Roaming\\npm\\node_modules\\pnpm\\lib\\node_modules\\@zkochan\\npm-lifecycle\\lib\\spawn.js:55:14)\n at ChildProcess.emit (events.js:198:13)\n at maybeClose (internal/child_process.js:982:16)\n at Process.ChildProcess._handle.onexit (internal/child_process.js:259:5)"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue