Home > Software design >  How to implement object rotation animation?
How to implement object rotation animation?

Time:07-25

There is a falling balls code https://jsfiddle.net/d1e8x7wk/,

which is generated using canvas

Code also added to this editor

window.addEventListener('load', () => {

  //---------------------------------------
  // 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 < 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 < 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 > 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 > 100) {
        if (ball.y < 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 > 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();



});
.section {
  position: relative;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: #000;
}
<div >
  <canvas id="balls"></canvas>
</div>

I'm trying to make a rotation the balls

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);
      };

After reading the information,

I wrote this code, but it does not work for me

ctx.rotate(this.angle * Math.PI / 180);

I must have misunderstood how to do this, tell me how to properly rotate the balls

Thanks in advance

CodePudding user response:

You will need to

  1. increase the angle at some point in your code
  2. apply a rotation transform to rotate around each ball center before each drawImage call
ctx.save();
ctx.translate( this.x,  this.y);
ctx.rotate(this.angle   * Math.PI / 180);
ctx.translate(-this.x, -this.y);
ctx.drawImage
ctx.restore()

ctx.save() and ctx.restore() is called to restore the canvas transform after each draw call.

Please read https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/rotate#rotating_a_shape_around_its_center for details on applying transforms to Canvas.

  • Related