简体   繁体   中英

Html5 canvas animation leaves a trail

I'm trying to make a little game to play around with canvas. I've decided to go for Flappy Bird as there are plenty of examples online. My goal was to make the game "responsive" for all devices. If you open it in a mobile or whichever screen, you should be able to play it normally.

For this purpose I drew a background image within the canvas that will adapt the height to the screen's height and repeat it through the x-axis.

蓬松的鸟

The issue I'm having is that, when I animate the bird, this one leaves a trail behind it.

As like so:

https://i.stack.imgur.com/xu0Vzm.png

An easy fix would be clearing the canvas as explained in this post:

Canvas image leaves weird trail when moving left

But it isn't possible because of the way I'm painting my background (I paint it once and repaint it if it resizes).

Question

How do I avoid this image trail with a static background? Is it maybe possible to have like a false static background that doesn't need to be repainted?

I obviously want to do it in the canvas, without needing to have an image in the html.

This is the code with the main functionality so that you can take a look:

 const cvs = document.getElementById("canvas"); const ctx = cvs.getContext("2d"); cvs.style.height = "100vh"; cvs.style.width = "100vw"; var bird = new Image(); var bg = new Image(); bird.src = "https://lh3.googleusercontent.com/prntPuix7QxlhKXx6IBtTxv7PrdEJtmzFJ4mopScNS1_klze86BVNy3PqHbQn2UQ4JyJdix3XQ=w128-h128-e365"; bg.src = "https://opengameart.org/sites/default/files/bg_5.png"; let gravity = 1; birdPositionY = 10; birdPositionX = 50; const draw = () => { ctx.drawImage(bird, birdPositionX, birdPositionY); birdPositionY += gravity; birdPositionX += 3; requestAnimationFrame(draw); }; const resizeCanvas = () => { const { width, height } = cvs.getBoundingClientRect(); cvs.height = height; cvs.width = width; let x = 0, y = 0; while (x < cvs.width && y < cvs.height) { ctx.drawImage(bg, x, y, 360, cvs.height); x += bg.width; } }; bg.onload = () => { resizeCanvas(); }; window.addEventListener("resize", resizeCanvas, false); draw(); 
 <!DOCTYPE html> <html> <head> <title>Flappy Bird</title> <meta name="viewport" content="width=device-width, user-scalable=no" /> <style> html { overflow: hidden; } body { margin: 0; } canvas { width: 100vw; height: 100vh; } </style> </head> <body> <canvas id="canvas"></canvas> <script src="index.js"></script> </body> </html> 

Hope it did make sense!

Thanks in advance!

Draw your background on a canvas that you will keep off-screen, then draw this canvas every frame on your visible one before you draw your moving parts.

 const cvs = document.getElementById("canvas"); const ctx = cvs.getContext("2d"); // our backround canvas that we won't append in the doc ("off-screen") const background = cvs.cloneNode(); const bg_ctx = background.getContext('2d'); cvs.style.height = "100vh"; cvs.style.width = "100vw"; var bird = new Image(); var bg = new Image(); bird.src = "https://lh3.googleusercontent.com/prntPuix7QxlhKXx6IBtTxv7PrdEJtmzFJ4mopScNS1_klze86BVNy3PqHbQn2UQ4JyJdix3XQ=w128-h128-e365"; bg.src = "https://opengameart.org/sites/default/files/bg_5.png"; let gravity = 1; birdPositionY = 10; birdPositionX = 50; const draw = () => { // first draw the background canvas ctx.drawImage(background, 0,0); // then your persona ctx.drawImage(bird, birdPositionX, birdPositionY); birdPositionY += gravity; birdPositionX += 3; requestAnimationFrame(draw); }; const resizeCanvas = () => { const { width, height } = cvs.getBoundingClientRect(); // resize both canvases cvs.height = background.height = height; cvs.width = background.width = width; let x = 0, y = 0; while (x < cvs.width && y < cvs.height) { // draw on the off-screen canvas bg_ctx.drawImage(bg, x, y, 360, cvs.height); x += bg.width; } }; bg.onload = () => { resizeCanvas(); draw(); }; window.addEventListener("resize", resizeCanvas, false); 
 <!DOCTYPE html> <html> <head> <title>Flappy Bird</title> <meta name="viewport" content="width=device-width, user-scalable=no" /> <style> html { overflow: hidden; } body { margin: 0; } canvas { width: 100vw; height: 100vh; } </style> </head> <body> <canvas id="canvas"></canvas> <script src="index.js"></script> </body> </html> 

I think you could just call resizeCanvas in your draw loop to redraw the background.

EDIT: Apparently that does not work. Another option is just drawing it in the draw loop like this:

const draw = () => {
  ctx.clearRect(0, 0, cvs.width, cvs.height)
  let x = 0, y = 0;
  while (x < cvs.width && y < cvs.height) {
    ctx.drawImage(bg, x, y, 360, cvs.height);
    x += bg.width;
  }

  ctx.drawImage(bird, birdPositionX, birdPositionY);

  birdPositionY += gravity;
  birdPositionX += 3;

  requestAnimationFrame(draw);
};

Edit: That did not work either. The other option would be to make two canvases and overlap them with one drawing the background and one drawing the bird.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM