Search code examples
typescripthtml5-canvasrequestanimationframe

request animation frame resetting an incremented variable each loop


I am trying to rotate an image on HTML canvas by 40, 1 degree at a time as part of a request animation frame loop. However, once ran, the amount the image rotates changes seemingly randomly on every refresh of the page, occasionally working exactly how I want it. Upon debugging, the 'counter' variable appears to be resetting each loop, despite being incremented. I'm at a loss as to what could cause this, new to the language, any help would be appreciated.

  let counter = 0;  //sets counter to 0

requestAnimationFrame(spin)  //initialises the loop

function spin(){

    rc.clearRect(0,0,window.innerWidth, window.innerHeight)
    rc.save();
    rc.translate(frameWidth/2, frameHeight/2);       //translates canvas for rotation (rc is canvas context)
    const angle = -1;     //angle to rotate by each loop
    rc.rotate(radCalc(angle));      //actual rotation (function is angle * PI/180)

    counter++  //Counter should keep track of each loop, yet resets every time?

    rc.translate(-frameWidth/2, -frameHeight/2)   //restore canvas
    rc.drawImage(dialSpinImage, column * frameWidth, row *frameHeight, frameWidth, frameHeight, 0, 0, frameWidth, frameHeight )   //draw image
    rc.restore

    let stop = requestAnimationFrame(spin);  //get animation frame ID

    if (counter < 40){   //checks canvas has not rotated over 40 degrees

        requestAnimationFrame(spin)

    }

    else {    //if it has, stop the loop

        cancelAnimationFrame(stop)

    }

}

}


Solution

  • There are a couple of smaller errors in your code. Let's get through all of 'em:

    1

    Inside the function spin() is the following line:

    const angle = -1;
    

    This ultimately resets angle to -1 with every invocation of spin(). The variable needs to be defined outside the scope of the function and just decremented inside.

    2

    let stop = requestAnimationFrame(spin);  //get animation frame ID
    
    if (counter < 40){   //checks canvas has not rotated over 40 degrees
    
        requestAnimationFrame(spin)
    
    }
    

    This doesn't work like you might expect. The first line doesn't just return a frame ID you might eventually use - no - it calls the requested function spin() right away. Like the angle variable it needs to be defined once outside the function.

    With all of the above in mind, here's a working example of your code:

    let canvas = document.getElementById("canvas");
    let rc = canvas.getContext("2d");
    let dialSpinImage = new Image();
    let frameWidth = canvas.width;
    let frameHeight = canvas.height;
    let angle = 0;
    let reqAnim;
    let counter = 0;
    
    dialSpinImage.onload = function() {
      start();
    }
    dialSpinImage.src = "https://picsum.photos/id/237/60/60";
    
    function start() {
      reqAnim = requestAnimationFrame(spin);
    }
    
    function spin() {
      rc.clearRect(0, 0, frameWidth, frameHeight);
      rc.save();
      rc.translate(frameWidth / 2, frameHeight / 2); //translates canvas for rotation (rc is canvas context)
      angle -= 1;
      rc.rotate(angle * Math.PI / 180);
      counter++;
    
    
      rc.translate(-frameWidth / 2, -frameHeight / 2) //restore canvas
      rc.drawImage(dialSpinImage, 0, 0, frameWidth, frameHeight) //draw image
      rc.restore();
    
    
    
      if (counter < 40) { //checks canvas has not rotated over 40 degrees
    
        reqAnim = requestAnimationFrame(spin)
    
      } else { //if it has, stop the loop
    
        cancelAnimationFrame(reqAnim);
     
      }
    
    }
    <canvas id="canvas"></canvas>