title: How to implement object rotation animation?
body: <p>There is a falling balls code <a href="https://jsfiddle.net/d1e8x7wk/" rel="nofollow noreferrer">https://jsfiddle.net/d1e8x7wk/</a>,</p>
  <p>which is generated using canvas</p>
  <p>Code also added to this editor</p>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>window.addEventListener('load', () =&gt; {
  
    //---------------------------------------
    // Set up ball options
    //---------------------------------------
  
    const imgBalls = [
      'https://cdn.iconscout.com/icon/premium/png-256-thumb/basketball-2500972-2093649.png',
      'https://cdn.iconscout.com/icon/premium/png-256-thumb/basketball-2500972-2093649.png',
      'https://cdn.iconscout.com/icon/premium/png-256-thumb/basketball-2500972-2093649.png',
      'https://cdn.iconscout.com/icon/premium/png-256-thumb/basketball-2500972-2093649.png',
      'https://cdn.iconscout.com/icon/premium/png-256-thumb/basketball-2500972-2093649.png',
      'https://cdn.iconscout.com/icon/premium/png-256-thumb/basketball-2500972-2093649.png'
    ]
  
    let ballCount = imgBalls.length, // How many balls
      DAMPING = 0.4, // Damping
      GRAVITY = 0.01, // Gravity strength
      SPEED = 5, // Ball speed
      ballAdditionTime = 100, // How fast are balls added
      ballSrc = imgBalls, // Ball image source
      topOffset = 400, // Adjust this for initial ball spawn point
      xOffset = 0, // left offset
      yOffset = 0, // bottom offset
      ballDensity = 20, // How dense are the balls
      ball_1_size = 200, // Ball 1 size
      ball_2_size = 180, // Ball 2 size
      ball_3_size = 62, // Ball 6 size
      canvasWidth = 1500, // Canvas width
      canvasHeight = 1000, // Canvas height
      stackBall = true, // Stack the balls (or false is overlap)
      ballsLoaded = 0,
      stopAnimation = false;
  
  
    //---------------------------------------
    // Canvas globals
    //---------------------------------------
  
    let canvas,
      ctx,
      TWO_PI = Math.PI * 2,
      balls = [],
      vel_x,
      vel_y;
  
    let rect = {
      x: 0,
      y: 0,
      w: canvasWidth,
      h: canvasHeight
    };
  
    //---------------------------------------
    // do the animation
    //---------------------------------------
  
    window.requestAnimFrame =
      window.requestAnimationFrame ||
      window.webkitRequestAnimationFrame ||
      function(callback) {
        window.setTimeout(callback, ballAdditionTime);
      };
  
    //---------------------------------------
    // set up the ball
    //---------------------------------------
  
    const Ball = function(x, y, radius, num) {
      this.x = x;
      this.y = y;
      this.px = x;
      this.py = y;
      this.fx = 0;
      this.fy = 0;
      this.radius = radius;
      this.num = num;
      this.angle = 0;
  
      // Different ball sizes
      let random = Math.round(Math.random() * imgBalls.length)
  
      if (random === 0) {
        this.width = ball_1_size;
        this.height = ball_1_size;
        if (stackBall) {
          this.radius = ball_1_size / 2;
        }
      } else if (random === 1 || random === 2) {
        this.width = ball_2_size;
        this.height = ball_2_size;
        if (stackBall) {
          this.radius = ball_2_size / 2;
        }
      } else {
        this.width = ball_3_size;
        this.height = ball_3_size;
        if (stackBall) {
          this.radius = ball_3_size / 2;
        }
      }
  
      ctx.rotate(this.angle * Math.PI / 180);
    };
  
    //---------------------------------------
    // Apply the physics
    //---------------------------------------
  
    Ball.prototype.apply_force = function(delta) {
      delta *= delta;
      this.fy += GRAVITY;
      this.x += this.fx * delta;
      this.y += this.fy * delta;
      this.fx = this.fy = 0;
    };
  
    //---------------------------------------
    // Newtonian motion algorithm
    //---------------------------------------
  
    Ball.prototype.velocity = function() {
      var nx = this.x * 2 - this.px;
      var ny = this.y * 2 - this.py;
      this.px = this.x;
      this.py = this.y;
      this.x = nx;
      this.y = ny;
    };
  
    //---------------------------------------
    // Ball prototype
    //---------------------------------------
  
    Ball.prototype.draw = function(ctx) {
      img = new Image();
      img.src = imgBalls[this.num];
      if (stackBall) {
        ctx.drawImage(
          img,
          this.x - this.radius - xOffset,
          this.y - this.radius - xOffset,
          this.width,
          this.height
        );
      } else {
        ctx.drawImage(
          img,
          this.x - xOffset,
          this.y - yOffset,
          this.width,
          this.height
        );
      }
    };
  
    //---------------------------------------
    // resolve collisions (ball on ball)
    //---------------------------------------
  
    let resolve_collisions = function(ip) {
      let i = balls.length;
      while (i--) {
        let ball_1 = balls[i];
        let n = balls.length;
        while (n--) {
          if (n == i) continue;
          let ball_2 = balls[n];
          let diff_x = ball_1.x - ball_2.x;
          let diff_y = ball_1.y - ball_2.y;
          let length = diff_x * diff_x + diff_y * diff_y;
          let dist = Math.sqrt(length);
          let real_dist = dist - (ball_1.radius + ball_2.radius);
  
          if (real_dist &lt; 0) {
            let vel_x1 = ball_1.x - ball_1.px;
            let vel_y1 = ball_1.y - ball_1.py;
            let vel_x2 = ball_2.x - ball_2.px;
            let vel_y2 = ball_2.y - ball_2.py;
            let depth_x = diff_x * (real_dist / dist);
            let depth_y = diff_y * (real_dist / dist);
            ball_1.x -= depth_x * 0.5;
            ball_1.y -= depth_y * 0.5;
            ball_2.x += depth_x * 0.5;
            ball_2.y += depth_y * 0.5;
  
            if (ip) {
              let pr1 = (DAMPING * (diff_x * vel_x1 + diff_y * vel_y1)) / length;
              let pr2 = (DAMPING * (diff_x * vel_x2 + diff_y * vel_y2)) / length;
              vel_x1 += pr2 * diff_x - pr1 * diff_x;
              vel_x2 += pr1 * diff_x - pr2 * diff_x;
              vel_y1 += pr2 * diff_y - pr1 * diff_y;
              vel_y2 += pr1 * diff_y - pr2 * diff_y;
              ball_1.px = ball_1.x - vel_x1;
              ball_1.py = ball_1.y - vel_y1;
              ball_2.px = ball_2.x - vel_x2;
              ball_2.py = ball_2.y - vel_y2;
            }
          }
        }
      }
    };
  
    //---------------------------------------
    // Bounce off the walls
    //---------------------------------------
  
    let check_walls = function() {
      let i = balls.length;
      while (i--) {
        let ball = balls[i];
  
        if (ball.x &lt; ball.radius) {
          let vel_x = ball.px - ball.x;
          ball.x = ball.radius;
          ball.px = ball.x - vel_x * DAMPING;
        } else if (ball.x + ball.radius &gt; canvas.width) {
          vel_x = ball.px - ball.x;
          ball.x = canvas.width - ball.radius;
          ball.px = ball.x - vel_x * DAMPING;
        }
  
        // Ball is new. So don't do collision detection until it hits the canvas. (with an offset to stop it snapping)
        if (ball.y &gt; 100) {
          if (ball.y &lt; ball.radius) {
            let vel_y = ball.py - ball.y;
            ball.y = ball.radius;
            ball.py = ball.y - vel_y * DAMPING;
          } else if (ball.y + ball.radius &gt; canvas.height) {
            vel_y = ball.py - ball.y;
            ball.y = canvas.height - ball.radius;
            ball.py = ball.y - vel_y * DAMPING;
          }
        }
      }
    };
  
    //---------------------------------------
    // Add a ball to the canvas
    //---------------------------------------
  
    let add_ball = function(num) {
      let x = Math.random() * canvas.width;
      let y = Math.random() * canvas.height;
      let r = 30 + Math.random() * ballDensity;
  
      let diff_x = x;
      let diff_y = y;
      let dist = Math.sqrt(diff_x * diff_x + diff_y * diff_y);
  
      balls.push(new Ball(x, y, r, num));
    };
  
  
    //---------------------------------------
    // iterate balls
    //---------------------------------------
  
    let update = function() {
      let iter = 1;
      let delta = SPEED / iter;
  
      while (iter--) {
        let i = balls.length;
        while (i--) {
          balls[i].apply_force(delta);
          balls[i].velocity();
        }
  
        resolve_collisions();
        check_walls();
  
        i = balls.length;
        while (i--) {
          balls[i].velocity();
          let ball = balls[i];
        }
  
        resolve_collisions();
        check_walls();
      }
  
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      i = balls.length;
      while (i--) {
        balls[i].draw(ctx);
      }
  
      requestAnimFrame(update);
    };
  
    //---------------------------------------
    // Set up the canvas object
    //---------------------------------------
  
    function doBalls() {
      stopAnimation = false;
      canvas = document.getElementById("balls");
      ctx = canvas.getContext("2d");
      let $canvasDiv = document.querySelector(".section");
  
      function respondCanvas() {
        canvas.height = $canvasDiv.clientHeight;
        canvas.width = $canvasDiv.clientWidth;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
      }
      respondCanvas();
  
      ballAdd();
    }
  
    function ballAdd() {
      let count = 0;
      let timer = setInterval(function() {
        addBallTimer();
      }, 100);
  
      let addBallTimer = function() {
        add_ball(count % ballCount);
        count++;
  
        if (count === ballCount) {
          stopTimer();
        }
      };
  
      let stopTimer = function() {
        clearInterval(timer);
      };
  
      update();
    }
  
    doBalls();
  
  
  
  });</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>.section {
    position: relative;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background-color: #000;
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;div class="section"&gt;
    &lt;canvas id="balls"&gt;&lt;/canvas&gt;
  &lt;/div&gt;</code></pre>
  </div>
  </div>
  </p>
  <p>I'm trying to make a rotation the balls</p>
  <pre><code>const Ball = function(x, y, radius, num) {
          this.x = x;
          this.y = y;
          this.px = x;
          this.py = y;
          this.fx = 0;
          this.fy = 0;
          this.radius = radius;
          this.num = num;
          this.angle = 0;
  
          // Different ball sizes
          let random = Math.round(Math.random() * imgBalls.length)
  
          if (random === 0) {
    this.width = ball_1_size;
    this.height = ball_1_size;
    if (stackBall) {
      this.radius = ball_1_size / 2;
    }
  } else if (random === 1 || random === 2) {
    this.width = ball_2_size;
    this.height = ball_2_size;
    if (stackBall) {
      this.radius = ball_2_size / 2;
    }
  } else {
    this.width = ball_3_size;
    this.height = ball_3_size;
    if (stackBall) {
      this.radius = ball_3_size / 2;
    }
  }
  
          ctx.rotate(this.angle * Math.PI / 180);
        };
  </code></pre>
  <p>After reading the information,</p>
  <p>I wrote this code, but it does not work for me</p>
  <pre><code>ctx.rotate(this.angle * Math.PI / 180);
  </code></pre>
  <p>I must have misunderstood how to do this, tell me how to properly rotate the balls</p>
  <p>Thanks in advance</p>