Search code examples
javascripthtml5-canvascollision-detection

resizing bouncing box at edges fails in canvas


I saw this video, and tried to do the same in a canvas

Here's the code (without size increment)

canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");

canvas.width = parseInt(window.innerWidth, 10);
canvas.height = parseInt(window.innerHeight, 10);

let x = 0;
let y = 0;
let size = 50;
let dx = 5;
let dy = 5;
let hue = 0;

function animate()
{
    x += dx;
    y += dy;

    hue = (hue + 10) % 360;

    if(x <= 0 || x >= canvas.width - size)
    {
        dx = -dx;
    }

    if(y <= 0 || y >= canvas.height - size)
    {
        dy = -dy;
    }

    ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
    ctx.fillRect(x, y, size, size);
    ctx.strokeStyle = 'black';
    ctx.lineWidth = 1;
    ctx.strokeRect(x, y, size, size);

    window.requestAnimationFrame(animate);
}

animate();
<canvas id="canvas"></canvas>

It's working fine, but when I add the increase part then the square gets stuck on the edge and increases exponentially:

canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");

canvas.width = parseInt(window.innerWidth, 10);
canvas.height = parseInt(window.innerHeight, 10);

let x = 0;
let y = 0;
let size = 50;
let dx = 5;
let dy = 5;
let hue = 0;

function animate()
{
    x += dx;
    y += dy;

    hue = (hue + 10) % 360;

    if(x <= 0 || x >= canvas.width - size)
    {
        dx = -dx;
    size = Math.ceil(size * 1.10);
    }

    if(y <= 0 || y >= canvas.height - size)
    {
        dy = -dy;
    size = Math.ceil(size * 1.10);
    }

    ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
    ctx.fillRect(x, y, size, size);
    ctx.strokeStyle = 'black';
    ctx.lineWidth = 1;
    ctx.strokeRect(x, y, size, size);

    window.requestAnimationFrame(animate);
}

animate();
<canvas id="canvas"></canvas>

Depending on size increment, it get stuck at different edges and at different number of bounces.

I think I have to evaluate something more in the edge detection, but I can figure it out.


Solution

  • This is a rather classical problem. The following part of your code

    x += dx;
    y += dy;
    

    merely updates the square's position no matter what. That means it does neither take into account the future position of the square nor it's size - which increases over time.

    The fix is quite simple: before updating the square's on-screen position, check if the square will move outside at it's current pace. If so, position it at the edge of the border and reverse the speed.

    canvas = document.getElementById("canvas");
    ctx = canvas.getContext("2d");
    
    canvas.width = parseInt(window.innerWidth, 10);
    canvas.height = parseInt(window.innerHeight, 10);
    
    let x = 0;
    let y = 0;
    let size = 50;
    let dx = 5;
    let dy = 5;
    let hue = 0;
    
    
    function animate() {
      if (dx < 0) {
        if (x + dx < 0) {
          x = 0;
          dx = -dx;
          size = Math.ceil(size * 1.02);
        } else {
          x += dx;
        }
      } else {
        if (x + size + dx > canvas.width) {
          x = canvas.width - size;
          dx = -dx;
          size = Math.ceil(size * 1.02);
        } else {
          x += dx;
        }
      }
      if (dy < 0) {
        if (y + dy < 0) {
          y = 0;
          dy = -dy;
          size = Math.ceil(size * 1.02);
        } else {
          y += dy;
        }
      } else {
        if (y + size + dy > canvas.height) {
          y = canvas.height - size;
          dy = -dy;
          size = Math.ceil(size * 1.02);
        } else {
          y += dy;
        }
      }
    
      hue = (hue + 10) % 360;
    
    
    
      ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
      ctx.fillRect(x, y, size, size);
      ctx.strokeStyle = 'black';
      ctx.lineWidth = 1;
      ctx.strokeRect(x, y, size, size);
    
      window.requestAnimationFrame(animate);
    }
    
    animate();
    <canvas id="canvas"></canvas>