Untitled
unknown
typescript
2 years ago
12 kB
9
Indexable
// canvas.component.ts
import {
Component,
ElementRef,
AfterViewInit,
ViewChild,
EventEmitter,
Output,
Input,
} from '@angular/core';
import { Rectangle } from '../models/rectangle';
@Component({
selector: 'app-canvas',
templateUrl: './canvas.component.html',
styleUrls: ['./canvas.component.css'],
})
export class CanvasComponent implements AfterViewInit {
@ViewChild('canvas', { static: true })
canvasElementRef!: ElementRef<HTMLCanvasElement>;
@ViewChild('container', { static: true }) containerRef!: ElementRef<HTMLDivElement>;
@Output() rectangleDrawn = new EventEmitter<Rectangle>();
//ge the deleted rectangle index
@Input() deletedRectangle = new EventEmitter<number>();
index: number = 0;
pdfSrc: string = 'assets/test.pdf';
private canvas!: HTMLCanvasElement;
private ctx!: CanvasRenderingContext2D;
rectangles: Rectangle[] = [];
currentRectangle: Rectangle | null = null;
isDrawing = false;
rectangleColor = 'red';
private rectangleDeleted = false;
crossClickIndex: number | null = null;
//----- Rectangle Selector ------//
private activeRectangle: Rectangle | null = null;
private resizeHandle: string | null = null;
private selectedRectangleIndex: number | null = null;
private dragging = false;
private resizing = false;
private resizingEdge: string | null = null;
private resizingStartX = 0;
private resizingStartY = 0;
private resizingStartWidth = 0;
private resizingStartHeight = 0;
constructor() {}
ngOnInit(): void {
this.ngOnDeleteRectangle();
}
currentPage: number = 1;
numPages = 0;
changePage(pageChange: number): void {
const newPage = this.currentPage + pageChange;
this.currentPage = newPage;
}
afterLoadComplete(pdfData: any): void {
this.numPages = pdfData.numPages;
}
onFileSelected() {
let $img: any = document.querySelector('#file');
if (typeof FileReader !== 'undefined') {
let reader = new FileReader();
reader.onload = (e: any) => {
this.pdfSrc = e.target.result;
};
reader.readAsArrayBuffer($img.files[0]);
}
}
ngAfterViewInit(): void {
this.canvas = this.canvasElementRef.nativeElement;
this.ctx = this.canvas.getContext('2d')!;
this.drawRectangles();
this.ctx.lineWidth = 3.5;
this.adjustCanvasSize();
this.canvas.addEventListener('mousedown', (event) =>
this.onMouseDown(event)
);
this.canvas.addEventListener('mousemove', (event) =>
this.onMouseMove(event)
);
this.canvas.addEventListener('mouseup', () => this.onMouseUp());
this.canvas.addEventListener('mousemove', (event) => this.onRectangleMouseMove(event));
this.canvas.addEventListener('mouseup', () => this.onRectangleMouseUp());
//add keyboard events to resize mouse
}
private adjustCanvasSize(): void {
const container = this.containerRef.nativeElement;
const canvas = this.canvasElementRef.nativeElement;
// Set the canvas size to match its container size
canvas.width = container.clientWidth;
canvas.height = container.clientHeight;
}
private isPointInsideRectangle(
x: number,
y: number,
rectangle: Rectangle
): boolean {
return (
x >= rectangle.x &&
x <= rectangle.x + rectangle.width &&
y >= rectangle.y &&
y <= rectangle.y + rectangle.height
);
}
private findSelectedRectangle(x: number, y: number): number | null {
for (let i = this.rectangles.length - 1; i >= 0; i--) {
if (this.isPointInsideRectangle(x, y, this.rectangles[i])) {
return i;
}
}
return null;
}
ngOnDeleteRectangle(): void {
this.deletedRectangle.subscribe((index) => {
this.rectangles.splice(index, 1);
this.drawRectangles();
});
}
onMouseDown(event: MouseEvent): void {
const { offsetX, offsetY } = event;
this.selectedRectangleIndex = this.findSelectedRectangle(offsetX, offsetY);
if (this.selectedRectangleIndex !== null) {
this.dragging = false;
this.resizing = true;
this.resizingEdge = this.getResizingEdge(offsetX, offsetY, this.rectangles[this.selectedRectangleIndex]);
if (this.resizingEdge) {
this.resizingStartX = this.rectangles[this.selectedRectangleIndex].x;
this.resizingStartY = this.rectangles[this.selectedRectangleIndex].y;
this.resizingStartWidth = this.rectangles[this.selectedRectangleIndex].width;
this.resizingStartHeight = this.rectangles[this.selectedRectangleIndex].height;
}
} else {
this.crossClickIndex = this.onCrossClick(event);
if (this.crossClickIndex !== null) {
this.rectangles.splice(this.crossClickIndex, 1); // Delete the rectangle
this.drawRectangles(); // Redraw the canvas
} else {
this.isDrawing = true;
const { offsetX, offsetY } = event;
this.currentRectangle = { x: offsetX, y: offsetY, width: 0, height: 0 };
}
}
}
onMouseMove(event: MouseEvent): void {
if (!this.isDrawing || !this.currentRectangle) {
return;
}
const { offsetX, offsetY } = event;
this.currentRectangle.width = offsetX - this.currentRectangle.x;
this.currentRectangle.height = offsetY - this.currentRectangle.y;
this.drawRectangles();
}
onMouseUp(): void {
if (!this.currentRectangle) {
return;
}
this.isDrawing = false;
this.rectangles.push(this.currentRectangle);
this.rectangleDrawn.emit({ ...this.currentRectangle }); // Emit the rectangle coordinates
this.currentRectangle = null;
this.drawRectangles();
}
private drawRectangles(): void {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.strokeStyle = this.rectangleColor;
this.rectangles.forEach((rectangle, index) => {
this.ctx.strokeStyle =
this.selectedRectangleIndex === index ? 'blue' : this.rectangleColor;
this.ctx.beginPath();
this.ctx.rect(
rectangle.x,
rectangle.y,
rectangle.width,
rectangle.height
);
this.ctx.stroke();
this.drawCrossMark(rectangle);
});
if (this.currentRectangle) {
this.ctx.beginPath();
this.ctx.rect(
this.currentRectangle.x,
this.currentRectangle.y,
this.currentRectangle.width,
this.currentRectangle.height
);
this.ctx.stroke();
}
}
private drawCrossMark(rectangle: Rectangle): void {
// Draw the cross mark on the top right edge
const crossSize = 12;
const crossX = rectangle.x + rectangle.width - crossSize / 2;
const crossY = rectangle.y - crossSize / 2;
this.ctx.strokeStyle = 'black';
this.ctx.beginPath();
this.ctx.moveTo(crossX, crossY);
this.ctx.lineTo(crossX + crossSize, crossY + crossSize);
this.ctx.moveTo(crossX, crossY + crossSize);
this.ctx.lineTo(crossX + crossSize, crossY);
this.ctx.stroke();
}
private onCrossClick(event: MouseEvent): number | null {
const { offsetX, offsetY } = event;
// Check if the click is on any cross mark
for (let i = 0; i < this.rectangles.length; i++) {
const rectangle = this.rectangles[i];
const crossSize = 10;
const crossX = rectangle.x + rectangle.width;
const crossY = rectangle.y;
if (
offsetX >= crossX - crossSize &&
offsetX <= crossX + crossSize &&
offsetY >= crossY - crossSize &&
offsetY <= crossY + crossSize
) {
return i; // Return the index of the deleted rectangle
}
}
return null;
}
//----- Rectangle mouse events ------//
private getResizingEdge(x: number, y: number, rectangle: Rectangle): string | null {
const resizeThreshold = 5;
if (
x >= rectangle.x - resizeThreshold &&
x <= rectangle.x + resizeThreshold
) {
return 'w';
} else if (
x >= rectangle.x + rectangle.width - resizeThreshold &&
x <= rectangle.x + rectangle.width + resizeThreshold
) {
return 'e';
} else if (
y >= rectangle.y - resizeThreshold &&
y <= rectangle.y + resizeThreshold
) {
return 'n';
} else if (
y >= rectangle.y + rectangle.height - resizeThreshold &&
y <= rectangle.y + rectangle.height + resizeThreshold
) {
return 's';
}
return null;
}
private onCanvasMouseMove(event: MouseEvent): void {
const { offsetX, offsetY } = event;
if (this.dragging) {
if (this.selectedRectangleIndex !== null) {
const selectedRectangle = this.rectangles[this.selectedRectangleIndex];
const deltaX = offsetX - this.resizingStartX;
const deltaY = offsetY - this.resizingStartY;
selectedRectangle.x = this.resizingStartX + deltaX;
selectedRectangle.y = this.resizingStartY + deltaY;
this.drawRectangles();
}
} else if (this.resizing) {
if (this.selectedRectangleIndex !== null && this.resizingEdge) {
const selectedRectangle = this.rectangles[this.selectedRectangleIndex];
const deltaX = offsetX - this.resizingStartX;
const deltaY = offsetY - this.resizingStartY;
if (this.resizingEdge.includes('n')) {
this.canvas.style.cursor = 'nw-resize';
selectedRectangle.y = this.resizingStartY + deltaY;
selectedRectangle.height = this.resizingStartHeight - deltaY;
}
if (this.resizingEdge.includes('s')) {
this.canvas.style.cursor = 'ne-resize';
selectedRectangle.height = this.resizingStartHeight + deltaY;
}
if (this.resizingEdge.includes('w')) {
this.canvas.style.cursor = 'sw-resize';
selectedRectangle.x = this.resizingStartX + deltaX;
selectedRectangle.width = this.resizingStartWidth - deltaX;
}
if (this.resizingEdge.includes('e')) {
this.canvas.style.cursor = 'se-resize';
selectedRectangle.width = this.resizingStartWidth + deltaX;
}
this.drawRectangles();
}
} else {
this.resizingEdge = null;
if (this.selectedRectangleIndex !== null) {
const selectedRectangle = this.rectangles[this.selectedRectangleIndex];
if (this.isPointInsideRectangle(offsetX, offsetY, selectedRectangle)) {
//this.canvas.style.cursor = 'move';
} else if (this.isPointInsideRectangle(offsetX, offsetY, { x: selectedRectangle.x, y: selectedRectangle.y, width: 5, height: 5 })) {
this.canvas.style.cursor = 'nw-resize';
} else if (this.isPointInsideRectangle(offsetX, offsetY, { x: selectedRectangle.x + selectedRectangle.width - 5, y: selectedRectangle.y, width: 5, height: 5 })) {
this.canvas.style.cursor = 'ne-resize';
} else if (this.isPointInsideRectangle(offsetX, offsetY, { x: selectedRectangle.x, y: selectedRectangle.y + selectedRectangle.height - 5, width: 5, height: 5 })) {
this.canvas.style.cursor = 'sw-resize';
} else if (this.isPointInsideRectangle(offsetX, offsetY, { x: selectedRectangle.x + selectedRectangle.width - 5, y: selectedRectangle.y + selectedRectangle.height - 5, width: 5, height: 5 })) {
this.canvas.style.cursor = 'se-resize';
} else {
this.canvas.style.cursor = 'default';
}
} else {
this.canvas.style.cursor = 'default';
}
}
}
private onCanvasMouseUp(): void {
this.dragging = false;
this.resizing = false;
}
private onRectangleMouseMove(event: MouseEvent): void {
this.onCanvasMouseMove(event);
}
private onRectangleMouseUp(): void {
this.onCanvasMouseUp();
}
}
Editor is loading...