Untitled
typescript
2 months ago
12 kB
1
Indexable
Never
// 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(); } }