How do I make a fully controllable bound box canvas element?

So in a way, Im trying to make a canvas element completely controllable via code and buttons, and I want it to look almost like fake physics, with a ball say... rolling left and right, bouncing off the walls, slowly losing its upward momentum, but continuing to move left and right without need of force.

I already managed to create a bouncy ball simulator that made a ball that moved Right, and bounced, continuing to move right forever, while losing its upward momentum. The problem is, there was no physics, and I didnt know how to make such things as physics possible.

Im not going to bother explaining small bits of it, so I will include a full example: when run with a canvas#canvas, it would work as I made it.

// initialise canvas and context
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d'); 
 
// physical variables
var g = 0.1; // gravity
var fac = 0.9; // velocity reduction factor per bounce
var radius = 20; // ball radius
var color = "Red"; // ball color
 
// initialise position and velocity of ball
var x = 50;
var y = 50;
var vx = 2;
var vy = 0;
 
// ensure that code does not run before page has loaded
window.onload = init; 
 
function init() {
// set up a timer
    setInterval(update, 1000/60); // 60 frames per second
};
 
function update() {
  // update velocity
  vy += g; // gravity
 
  // update position
  x += vx;
  y += vy; 
 
  // handle bouncing
  if (y > canvas.height - radius){
    y = canvas.height - radius;
    vy *= -fac;
  }
 
  // wrap around
  if (x > canvas.width + radius){
    x = -radius;
    vx = -vx;
  }
 
  // update the ball
  drawBall();
};
 
function drawBall() {
    with (context){
        clearRect(0, 0, canvas.width, canvas.height); // clear canvas
        fillStyle = color;
        beginPath();
        arc(x, y, radius, 0, 2*Math.PI, true);
        closePath();
        fill();
    };
};

function BounceUp () {
 vy += 5;
}

I tried making it so it would bound off the edges of the canvas, but I didn’t succeed, and it mostly broke the position of the ball, so I didn’t include that bit of code here.

1 answer

  • answered 2019-06-11 23:01 Taylor Spark

    codepen.io

    View the project above for a more interactive answer.

    You showed that you obviously made the right idea with your coordinates. The problem is that you most likely didn't use your X coordinates correctly the first time you tried.

    Like adding this below "//Handle Bouncing":

       if (x > canvas.width - radius){
        x = canvas.width - radius;
        vx *= -fac;
      }
    

    What this does is use your "bouncing" mechanism against the right wall, giving you 2 solid faces now (Ground and right wall) and it reverses your velocity direction, giving you a "Bounding off the wall" effect in which it will head towards the left wall.

    Next we need to solidify the Left wall:

       if (x < 1 + radius){
        x = radius;
        vx = -vx;
      }
    

    This makes it, so when it detects that the edge of the circle has reached 1 of the `canvas.width" it will reverse the velocity direction AGAIN, thus causing it to bounce back toward the right wall, while also conserving momentum.

    Finally we need to make the roof solid, this wasnt very difficult, just copy the same design above but adapt it for your Y coordinate:

        if (y < 1 + radius){
        y = radius;
        vy = -vy;
      }
    

    Like before, when the ball touches the top of the height, it detects it, and reverses the direction of momentum (velocity-Y) and CONSERVES it, that way your not infinitely bouncing and speeding up.

    And for an extra detail, I added a bounce button to make it jump (Only use it when the arch is going down, or when the ball is falling back down from a bounce): Just a simple function adding a little force to your velocity:

    function BounceUp () {
     vy += 5;
    }
    

    it can thus be called with a button via onclick="" :)

    Final Product: (view in fullpage mode)

    // initialize canvas and context
    var canvas = document.getElementById('canvas');
    var context = canvas.getContext('2d'); 
     
    // physical variables
    var g = 0.1; // gravity
    var fac = 0.9; // velocity reduction factor per bounce
    var radius = 20; // ball radius
    var color = "Red"; // ball color
     
    // initialise position and velocity of ball
    var x = 50;
    var y = 50;
    var vx = 5;
    var vy = 0;
     
    // ensure that code does not run before page has loaded
    window.onload = init; 
     
    function init() {
    // set up a timer
        setInterval(update, 1000/60); // 60 frames per second
    };
     
    function update() {
      // update velocity
      vy += g; // gravity
     
      // update position
      x += vx;
      y += vy; 
     
      // handle bouncing / ground and right physics
      if (y > canvas.height - radius){
        y = canvas.height - radius;
        vy *= -fac;
      }
       if (x > canvas.width - radius){
        x = canvas.width - radius;
        vx *= -fac;
      }
     
      // left and roof physics
      if (x > canvas.width + radius){
        x = -radius;
        vx = -vx;
      }
       if (x < 1 + radius){
        x = radius;
        vx = -vx;
      }
        if (y < 1 + radius){
        y = radius;
        vy = -vy;
      }
     
      // update the ball
      drawBall();
    };
     
    function drawBall() {
        with (context){
            clearRect(0, 0, canvas.width, canvas.height); // clear canvas
            fillStyle = color;
            beginPath();
            arc(x, y, radius, 0, 2*Math.PI, true);
            closePath();
            fill();
        };
    };
    function BounceUp () {
     vy += 5;
    }
    body{background-color:#666;}
    #canvas{background-color:#ccc;}
    <canvas id="canvas" width="1000" height="300" ></canvas><br>
    <button onclick="BounceUp()">Bounce</button>