Bonked
Bonked

Reputation: 39

Clearing previous positions of canvas object and not the entire canvas

l believe to have a logic error in the way of which my logic is meant to find the previous coordinates of my canvas object, a moving image, and delete the frame drawn before it so that it does not duplicated the image every time it is drawn onto the canvas.

There is a reproducible demo below and l added comments where l believe the problem occurs.

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

var imgTag = new Image();
var X_POS = canvas.width;
var Y_POS = 0;

imgTag.src = "https://i.sstatic.net/Rk0DW.png"; // load image

function animate() {

  /* Error resides from here */
  var coords = {};
  coords.x = Math.floor(this.X_POS - imgTag);
  coords.y = Math.floor(this.Y_POS - imgTag);

  ctx.clearRect(coords.x, coords.y, X_POS, Y_POS);
  /* To here */


  ctx.drawImage(imgTag, X_POS, Y_POS);
  X_POS -= 5;
  if (X_POS > 200) requestAnimationFrame(animate)
}


window.onload = function() {

  ctx.drawImage(the_background, 0, 0, canvas.width, canvas.height);
  animate();
}
html,
body {
  width: 100%;
  height: 100%;
  margin: 0px;
  border: 0;
  overflow: hidden;
  display: block;
}
<html>
<canvas id="c" width="600" height="400"></canvas>

<img style="display: none;" id="the_button" src="https://i.imgur.com/wO7Wc2w.png" />

<img style="display: none;" id="the_background" src="https://img.freepik.com/free-photo/hand-painted-watercolor-background-with-sky-clouds-shape_24972-1095.jpg?size=626&ext=jpg" />

</html>

Upvotes: 1

Views: 150

Answers (1)

ggorlen
ggorlen

Reputation: 56885

It seems logical to only clear the subset of the canvas that's changing, but the normal approach is to clear and redraw the entire canvas per frame. After the car moves, the background that's cleared underneath it needs to be filled in, and trying to redraw only that subset will lead to visual artifacts and general fussiness. Clearing small portions of the screen is a premature optimization.

Canvases can't keep track of much of anything other than pixels, so an animation is more like a flipbook of stationary frames and less like a cardboard cutout animation, where the same pieces of cardboard move along and overlap one another.

Math.floor(this.X_POS - imgTag) looks wrong -- imgTag is an image object, which doesn't make sense to subtract from a number. You may have meant to grab the x property from this.

Use image.onload to ensure the image is actually loaded before running the loop. It's a bit odd to use image tags in the DOM just to load images for a canvas. I'd do that programmatically from JS which saves some typing and makes it easier to manage the data.

const loadImg = url => new Promise((resolve, reject) => {
  const img = new Image();
  img.onerror = reject;
  img.onload = () => resolve(img);
  img.src = url;
});

const images = [
  "https://img.freepik.com/free-photo/hand-painted-watercolor-background-with-sky-clouds-shape_24972-1095.jpg?size=626&ext=jpg",
  "https://i.sstatic.net/Rk0DW.png",
];
Promise
  .all(images.map(loadImg))
  .then(([backgroundImg, carImg]) => {
    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
    const car = {x: canvas.width, y: 0};
    
    (function update() {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      
      ctx.drawImage(
        backgroundImg, 0, 0, canvas.width, canvas.height
      );
      ctx.drawImage(
        carImg, car.x, car.y, carImg.width, carImg.height
      );
      car.x -= 5;
      
      if (car.x > 200) {
        requestAnimationFrame(update);
      }
    })();
  })
  .catch(err => console.error(err))
;
<canvas id="canvas" width="600" height="400"></canvas>

Upvotes: 1

Related Questions