Moving/Scrolling/Sliding Background in HTML5 Canvas

Sometimes your game or canvas experiment might have one or more layers of animating backgrounds that are set in motion for the player’s movement or some other reason (like creating a parallax effect?). Just had to do something similar the other day. The problem is tricky but with a bit of thinking it seems like I came up with a proper solution!

Problem

It is important to understand the problem first. A quick demo (refresh it):

What's the one thing every developer wants? More screens! Enhance your coding experience with an external monitor to increase screen real estate.

The code ain’t something you haven’t seen before.

// Velocity X
var vx = 0;

var img = new Image();
img.src = 'http://cssdeck.com/uploads/media/items/4/4OIJyak.png';

(function renderGame() {
	window.requestAnimationFrame(renderGame);
	
	ctx.clearRect(0, 0, W, H);
	
	ctx.fillStyle = '#333';
	ctx.fillRect(0, 0, 500, 400);
	
	ctx.drawImage(img, vx, 50);
	
	vx -= 2;
}());

A quick fillRect followed by a drawImage. Note, the second argument passed to drawImage is a variable we define called vx that acts as the velocity or speed at which the background is going to slide on the X axis. The code used to decrease the constant velocity is vx -= 2;.

As you can see, the problem is that the background image starts sliding to the left and soon it quits the canvas. What we want instead is that, it should repeat itself along the X axis. So if first half of it has been passed off the left side of the canvas (or the right side), then we want that first half to appear in the second half of the screen.

Solution

First, I tried to solve this problem with ctx.createPattern(img, 'repeat-x'), but that doesn’t seems to be the correct way.

On trying harder, I found an interesting solution. Basically we need to drawImage() twice.

So the code that does the trick is this:

ctx.drawImage(img, vx, 50);
ctx.drawImage(img, img.width-Math.abs(vx), 50);

if (Math.abs(vx) > img.width) {
	vx = 0;
}

vx -= 2;

We draw our image normally like we were doing before with our constant velocity on the X axis but we draw it again at a position that is right after our first drawing. This is how we calculate the X position for the end of the first drawing which is where the second drawing begins img.width-Math.abs(vx).

As soon as our velocity is more than the image width, we re-set it to 0.

if (Math.abs(vx) > img.width) {
	vx = 0;
}

This way the first drawing is re-positioned back at the normal state, i.e., 0 and the second image appears after that. If we do not do this, then we’ll just see both the drawn images slide off the canvas.

Note: In the experiment above, you can see the separation (between the image) as it slides, although in real world cases like games, etc. you’ll be definitely having better graphics producing seamless effect, i.e., the demarcation won’t be noticed as the image is repeated along either axis.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Author: Rishabh

Rishabh is a full stack web and mobile developer from India. Follow me on Twitter.

9 thoughts on “Moving/Scrolling/Sliding Background in HTML5 Canvas”

  1. Rather keep the image object outside the loop function:

    var img = new Image();
    img.src = 'http://cssdeck.com/uploads/media/items/4/4OIJyak.png';

    Will save object creation everytime.

    1. Yes, by making changes to 2 lines –

      // ctx.drawImage(img, img.width-Math.abs(vx), 50); becomes
      ctx.drawImage(img, Math.abs(vx)-img.width, 50);
      // .. and ..
      // vx -= 2; becomes
      vx += 2;

    1. Making something scrollable is just about changing the x/y coordinates of the object(s) regularly at fixed intervals (for animation). All you need to figure out is what part of the object to show on the canvas/screen edges which shouldn’t be too hard if you keep on trying for sometime.

  2. Perhaps this is a ridiculously obvious question but I’ll ask it anyway.
    I’m kind of new to scripting and when I use the script my image always transforms. it either overstretches or parts of it don’t show up in my canvas.

    for example I used a canvas and image with the following specifications:

    canvas
    {
    display:block;
    position:absolute;
    top:5px;
    right:5px;
    width:560px;
    height:440px;
    z-index:-1;
    }

    Image dimensions: 1230×365

    When I load the website the canvas is in the right place, the image is scrolling but half of my canvas is black (top half) and the lower part shows the image scrolling but it is zoomed in or blown up out of proportions judging by the visible pixels.

    What am I doing wrong or which parts should I adjust? (I tried playing around with it but all I could manage was removing the black top banner or actually fitting the image but then it didn’t scroll anymore :S

  3. Aha! Thanks for helping me wrap my brain around this. Now I need to make some kind of cool panning game or something (was actually researching panning text on a much dryer subject but this helped).

    Thanks much,

    Chris

  4. i want a little help about this how i will make blending of two images in canvas and also moving one image right or left?can you help me?

Leave a Reply

Your email address will not be published. Required fields are marked *