Search code examples
angulartypescriptcanvasrequestanimationframecancelanimationframe

How to prevent requestAnimationFrame from redrawing canvas more then one time after clicking the button?


I am drawing canvas sin and adding movement to it

export class CanvasComponent implements OnInit {
  @ViewChild('canvas', { static: true })
  canvas: ElementRef<HTMLCanvasElement>;

  ctx: CanvasRenderingContext2D;

  winddrawvalue = 0;
  windvalue = 0;

  constructor() { }

  @Input() set speeddata(speed: number){
    this.windvalue = speed;
    this.drawWind();
  }

  ngOnInit(): void {
    this.ctx = this.canvas.nativeElement.getContext('2d');
  }

  drawWind() {
    requestAnimationFrame(this.drawWind.bind(this));
    const canvas = this.canvas.nativeElement;
    this.ctx.lineWidth = 2;
    this.ctx.clearRect(0, 0, canvas.width, canvas.height);
    this.ctx.beginPath();

    this.ctx.moveTo(-10, canvas.height / 2 - 12);
    for (let i = 0; i < canvas.width; i++) {
      this.ctx.lineTo(i, canvas.height / 2 - 12 + Math.sin(i * 0.04 + this.winddrawvalue) * 15);
    }

    this.ctx.moveTo(-10, canvas.height / 2);
    for (let i = 0; i < canvas.width; i++) {
      this.ctx.lineTo(i, canvas.height / 2 + Math.sin(i * 0.04 + this.winddrawvalue) * 15);
    }

    this.ctx.moveTo(-10, canvas.height / 2 + 12);
    for (let i = 0; i < canvas.width; i++) {
      this.ctx.lineTo(i, canvas.height / 2 + 12 + Math.sin(i * 0.04 + this.winddrawvalue) * 15);
    }

    this.ctx.stroke();
    this.winddrawvalue += this.windvalue;
  }

}

And everytime i am pressing button to draw it again canvas is redrawing it but it moves 2 times faster then before. I tried to do

request = requestAnimationFrame(this.drawWind.bind(this));

  @Input() set speeddata(speed: number){
    this.windvalue = speed;
    this.stopAnimation(this.request);
    this.drawWind();
  }

  stopAnimation(req) {
    cancelAnimationFrame(req);
  }

with cancelAnimationFrame() hoping that it will get requestID to stop the ongoing animation but it didn't work.


Solution

  • Since drawWind sets up the next call every time it's called, it makes sense that when you call it from the click handler, now you have two series of it running in parallel and things go twice as fast.

    You said you tried cancelAnimationFrame but didn't show us that code. That is indeed how you would deal with this:

    drawWind() {
      cancelAnimationFrame(this.rafHandle);
      this.rafHandle = requestAnimationFrame(this.drawWind.bind(this));
    

    Now, when the button click calls drawWind while it has an rAF callback scheduled, it cancels that callback and sets a new one. So you still only have the one series running.