JermQ TV
JermQ TV

Reputation: 23

How do I optimize JavaScript Canvas 2d Animation?

Can anyone help me optimize this animation I am trying to make.

Basically a car image sits in the foreground and the background moves behind it so it looks like the car is moving. It's pretty simple but it keeps getting very laggy and crashing.

document.addEventListener("fullscreenchange", function() {
  console.log('Fullscreen Change');
});

const canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const ctx = canvas.getContext("2d");

function drawCar(){
  ctx.drawImage(img, 20, canvas.height-200);
}

const backgroundFrames = [];

let img;
let interval;

let x = 0;

const patternCanvas = document.createElement('canvas');
const patternContext = patternCanvas.getContext('2d');
patternCanvas.width = 4096;
patternCanvas.height = 700;

    const patImg = new Image();
    patImg.addEventListener('load', function() {
      
      for (let i = 0; i >= -4096; i -= 16){
        patternContext.drawImage(patImg, i, 0);
        let k = i/-16;
        backgroundFrames[k] = ctx.createPattern(patternCanvas, 'repeat-x');
      }
      
      
      img = new Image();
      img.addEventListener('load', function(){
        setTimeout(function(){
          interval = setInterval(bckchg, 1000/30);
        }, 3000);
      }, false);
      img.src = 'img.png';

    }, false);
    patImg.src = 'background.jpg';

let l = 0;

function bckchg(){
  
  ctx.fillStyle = backgroundFrames[l];
  
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  
  drawCar();
  
  l++;
  
  if(l === 187){
    l = 0;
  }
}

canvas.addEventListener('click', function(){
  clearInterval(interval);
});

I've tried creating all the background images beforehand but its still pretty laggy and still crashes.

I know the code is pretty messy, I only started coding recently. Any and all help would be appreciated.

Edit:

Here is the HTML

<!doctype html>
<html>
  <head>
    <title>Drive</title>
    <link rel="stylesheet" href="drive1.css">
  </head>
  <body>
    <main>
      <canvas id="canvas">
      </canvas>
      <!--<img src="background.jpg">-->
      <!--<div id="menu">-->
      <!--  <button id="start">Start</button>-->
      <!--  <button id="stop">Stop</button>-->
      <!--  <input id="volSlider" type="range" name="vol" min="o" max="100">-->
      <!--  <label id="volLabel" for="vol">Volume</label>-->
        
      <!--  <form id="songForm">-->
      <!--    <input type="radio" id="td" name="song" value="td">-->
      <!--    <label for="td">Tokyo Drift</label>-->
      <!--    <br>-->
      <!--    <input type="radio" id="supra" name="song" value="supra">-->
      <!--    <label for="supra">Supra</label>-->
      <!--  </form>-->
  
      <!--</div>-->
    </main>
    <script src="drive_mk2.js">
    </script>
    <script src="drive_music.js"></script>
  </body>
</html>

Upvotes: 1

Views: 313

Answers (2)

Justin
Justin

Reputation: 2958

If you are looking for another method for a scrolling background here's something you can try. Create a class for your background and then create 2 instances of it. Once the first background scrolls off the right edge of the screen just set it's x back to canvas.width.

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
canvas.width = innerWidth;
canvas.height = innerHeight;

let bg = new Image();
bg.src = 'https://lh3.googleusercontent.com/TUeeuiH2XxHiKzH11POaAM7_K082QIou_0WRW5_LqW1BMAxKjnyTDX1bUju7sawS2o1g0Xp-536vJH0lEnRfE6SM_gBKmU85RAwoMdZhUMDD_WC1wg-FDfHxtzEWyjWixlg1SOZALQ=w2400';

class Background {
  constructor(x) {
    this.x = x;
    this.y = 0;
    this.w = canvas.width;
    this.h = canvas.height;
    this.vx = 0;
    this.vy = 0;
  }
  draw() {
    ctx.drawImage(bg, this.x, this.y, this.w, this.h)
  }
  update() {
    this.vx = -2;
    this.x += this.vx;
    if (this.x + this.w <= 1) this.x = canvas.width;
  }
}
let bg1 = new Background(0);
let bg2 = new Background(canvas.width)

function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  bg1.draw()
  bg2.draw()
  bg1.update()
  bg2.update()
  requestAnimationFrame(animate)
}
animate()
<canvas id="canvas"></canvas>

Upvotes: 0

IT goldman
IT goldman

Reputation: 19475

I didn't se the original HTML, but instead of setInterval you should use requestAnimationFrame which waits for next animation frame so animation is smooth. It's pretty much similar syntax but this happens around 60hz so you might want to use some time variable. See my recent answer here: https://stackoverflow.com/a/72821732/3807365

So in your case, change accordingly:

img.addEventListener('load', function() {
  setTimeout(function() {
    var loop = function() {
      bckchg();
      interval = requestAnimationFrame(loop);
    }
    loop();
  }, 3000);
}, false);

// and
canvas.addEventListener('click', function() {
  cancelAnimationFrame(interval);
});

Update: No answer about background scroll animation would be complete without a link to this answer https://stackoverflow.com/a/67305637/3807365

Upvotes: 1

Related Questions