Search code examples
javascriptangulartypescriptcanvashtml5-canvas

How to draw an rectangle on canvas element in Typescript(angular)?


I tried to draw an rectangle on canvas element using typescript(angular), but its not working as expected, at the same i created in javascript its working. Please look at the below code,

export class AppComponent {
  @Input() width = 512;
  @Input() height = 418;
  @ViewChild('canvas') canvas: ElementRef;
  cx: CanvasRenderingContext2D;
  drawingSubscription: Subscription;
  name = 'Angular ' + VERSION.major;

  ngAfterViewInit() {
    const canvasEl: HTMLCanvasElement = this.canvas.nativeElement;
    this.cx = canvasEl.getContext('2d');
    canvasEl.width = this.width;
    canvasEl.height = this.height;

    this.cx.lineWidth = 2;
    //this.cx.lineCap = 'round';

    this.captureEvents(canvasEl);
  }
  captureEvents(canvasEl: HTMLCanvasElement) {
    var startX;
    var startY;
    var mouseX;
    var mouseY;
    // this will capture all mousedown events from teh canvas element
    this.drawingSubscription = fromEvent(canvasEl, 'mousedown')
      .pipe(
        switchMap((e) => {
          // after a mouse down, we'll record all mouse moves
          return fromEvent(canvasEl, 'mousemove').pipe(
            // we'll stop (and unsubscribe) once the user releases the mouse
            // this will trigger a 'mouseup' event
            takeUntil(fromEvent(canvasEl, 'mouseup')),
            // we'll also stop (and unsubscribe) once the mouse leaves the canvas (mouseleave event)
            takeUntil(fromEvent(canvasEl, 'mouseleave')),
            // pairwise lets us get the previous value to draw a line from
            // the previous point to the current point
            pairwise()
          );
        })
      )
      .subscribe((res: [MouseEvent, MouseEvent]) => {
        const rect = canvasEl.getBoundingClientRect();
        console.log(res[0]);
        // previous and current position with the offset

        startX = res[0].clientX - rect.left;
        startY = res[0].clientY - rect.top;
        console.log(startX, startY);

        const prevPos = {
          x: res[0].clientX - rect.left,
          y: res[0].clientY - rect.top,
        };

        const currentPos = {
          x: res[1].clientX - rect.left,
          y: res[1].clientY - rect.top,
        };

        mouseX = res[0].clientX - rect.left;
        mouseY = res[0].clientY - rect.top;

        this.cx.clearRect(0, 0, canvasEl.width, canvasEl.height);

        var width = mouseX - startX;
        var height = mouseY - startY;

        this.cx.strokeRect(startX, startY, width, height);
        console.log(startX, startY, width, height);
        
      });
  }

 

  ngOnDestroy() {
    // this will remove event lister when this component is destroyed
    this.drawingSubscription.unsubscribe();
  }
}

Html code:

<canvas #canvas> </canvas>

Please check my live code on stackblitz : https://stackblitz.com/edit/angular-ivy-iqjbew?file=src/app/app.component.ts

And Working examples of Javascript : https://jsfiddle.net/sathishcst10/wp2anys7/12/


Solution

  • The JS code and the TS code are not the same. You have an error in your TS code.

    
            startX = res[0].clientX - rect.left;
            startY = res[0].clientY - rect.top;
    
            mouseX = res[0].clientX - rect.left;
            mouseY = res[0].clientY - rect.top;
    
            var width = mouseX - startX;
            var height = mouseY - startY;
    

    As you can see startX and mouseX are exactly the same, thus width and height will always be ZERO and the rect will not be visible.

    Here a quick way to fix this, but probably there are better ways

     this.drawingSubscription = fromEvent(canvasEl, 'mousedown')
          .pipe(
            tap((e) => {
              startX = null;
              startY = null;
            }),
            switchMap((e) => {
            // your code
            })
          )
          .subscribe((res: [MouseEvent, MouseEvent]) => {
            const rect = canvasEl.getBoundingClientRect();
            if (startX === null) {
              startX = res[0].clientX - rect.left;
              startY = res[0].clientY - rect.top;
            }
            // your code