Why when changing tabs sometimes canvas animation fails?

Viewed 279

I've created a simple JavaScript canvas animation, and I've noticed that when I change to another tab and then come back to the original tab, sometimes the box stays moving sticked to one border or stays quiet in one corner. It happens in small and big time periods.

I've tested it in Chrome and Firefox latest versions and I don't know what's causing the bug.

Here's the code in jsfiddle: link

And here's a code snippet:

var canvas = document.getElementById('animation');
      var ctx = canvas.getContext('2d');
      var startTime;
      var elapsed;
      var oldTimeStamp;
      var interval;
      function createBox(x, y, width, height, vx, vy, color) {
        this.x = x
        this.y = y
        this.width = width
        this.height = height
        this.vx = vx
        this.vy = vy
        this.color = color
        this.draw = function () {
          ctx.fillStyle = this.color
          ctx.fillRect(
            Math.max(Math.min(this.x, canvas.width-this.width), 0),
            Math.max(Math.min(this.y, canvas.height-this.height), 0),
            this.width,
            this.height
          );
        }
      }
      var red = new createBox(0, 0, 50, 75,  0.1, 0.2, "gray")
      function step(timeStamp) {
        // Time
        if (startTime === undefined) {
          startTime = timeStamp
          oldTimeStamp = timeStamp
        }
        elapsed = timeStamp - startTime;
        interval = timeStamp - oldTimeStamp
        if(interval > 150){
          interval = 150
        }
        oldTimeStamp = timeStamp
        // Draw
        ctx.fillStyle = 'rgba(255, 255, 255, 1)';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        if (red.y + red.vy > canvas.height-red.height || red.y + red.vy < 0) {
          red.vy = -red.vy;
        }
        if (red.x + red.vx > canvas.width-red.width || red.x + red.vx < 0) {
          red.vx = -red.vx;
        }
        red.x = red.x + red.vx * interval

        red.y = red.y + red.vy * interval
        red.draw()
        // Request new animation frame
        window.requestAnimationFrame(step);
      }
      window.requestAnimationFrame(step);
canvas {
  border: 1px solid black;
}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Canvas2-Bug</title>
  </head>
  <body>
    <canvas id="animation" width="300" height="300"></canvas>
  </body>
</html>

The questions are:

  • What's causing the bug
  • How can I fix it

Edits

  1. Udapte question with code from @theusaf that solves one issue which only happens in big time periods. (Changing time period to 150)
1 Answers

The reason this is happening is because requestAnimationFrame pauses until the user comes back to the tab, creating a large interval. This sets red.x and red.y to very large numbers. Then, the velocity of the box is constantly swapped, meaning that the box stays in the corner.

Ex: user leaves for a time, after the draw, red.x and red.y are numbers like 500

red.x/red.y + red.vx/red.vy will then always be greater than canvas.height/width - red.height/width

  • red.vx/red.vy then swaps from being negative to positive and back to negative each frame.

To fix this, you can add a condition to make sure that interval doesn't get too high (over 50):

var canvas = document.getElementById('animation');
      var ctx = canvas.getContext('2d');
      var startTime;
      var elapsed;
      var oldTimeStamp;
      var interval;
      function createBox(x, y, width, height, vx, vy, color) {
        this.x = x
        this.y = y
        this.width = width
        this.height = height
        this.vx = vx
        this.vy = vy
        this.color = color
        this.draw = function () {
          ctx.fillStyle = this.color
          ctx.fillRect(
            Math.max(Math.min(this.x, canvas.width-this.width), 0),
            Math.max(Math.min(this.y, canvas.height-this.height), 0),
            this.width,
            this.height
          );
        }
      }
      var red = new createBox(0, 0, 50, 75,  0.1, 0.2, "gray")
      function step(timeStamp) {
        // Time
        if (startTime === undefined) {
          startTime = timeStamp
          oldTimeStamp = timeStamp
        }
        elapsed = timeStamp - startTime;
        interval = timeStamp - oldTimeStamp;
        if(interval > 50){interval=50}
        oldTimeStamp = timeStamp
        // Draw
        ctx.fillStyle = 'rgba(255, 255, 255, 1)';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        if (red.y + red.vy > canvas.height-red.height || red.y + red.vy < 0) {
          red.vy = -red.vy;
        }
        if (red.x + red.vx > canvas.width-red.width || red.x + red.vx < 0) {
          red.vx = -red.vx;
        }
        red.x = red.x + red.vx * interval

        red.y = red.y + red.vy * interval
        red.draw()
        // Request new animation frame
        window.requestAnimationFrame(step);
      }
      window.requestAnimationFrame(step);
canvas {
  border: 1px solid black;
}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Canvas2-Bug</title>
  </head>
  <body>
    <canvas id="animation" width="300" height="300"></canvas>
  </body>
</html>

Related