Home > Software design >  How to draw an rectangle on canvas element in Typescript(angular)?
How to draw an rectangle on canvas element in Typescript(angular)?

Time:09-21

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/

CodePudding user response:

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

CodePudding user response:

I suggest not use pairwise, you can use simply tap to store the position in mouseDown. Futhermore, I like more use a merge from events mouseup and mouseleave with take(1)

More or less:

  captureEvents(canvasEl: HTMLCanvasElement) {
    const rect = canvasEl.getBoundingClientRect();
    const prevPos = {x:0,y:0};
    // this will capture all mousedown events from teh canvas element
    this.drawingSubscription = fromEvent(canvasEl, 'mousedown')
      .pipe(
        tap((e:MouseEvent)=>{
          prevPos.x= e.clientX - rect.left
          prevPos.y=e.clientY - rect.top
      }),
        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(
                 merge(fromEvent(canvasEl, 'mouseup'),
                       fromEvent(canvasEl, 'mouseleave'))
                .pipe(take(1))),
          );
        })
      )
      .subscribe((res:MouseEvent) => {

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


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

        const width = currentPos.x-prevPos.x;
        const height = currentPos.y-prevPos.y;

        this.cx.strokeRect(prevPos.x, prevPos.y, width, height);

        // this method we'll implement soon to do the actual drawing
       // this.drawOnCanvas(prevPos, currentPos);
      });
  }

NOTE: In typescript not use "var" to declare a variable else or "let" or "const"

  • Related