Untitled

mail@pastecode.io avatarunknown
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();
  }
}