14

I was wondering how I could go about making a camera-like view where I could scroll a level within a canvas element just like this:

http://playbiolab.com/

Biolab screenshot

Michael Currie
  • 13,721
  • 9
  • 42
  • 58
Oni Enzeru
  • 459
  • 2
  • 6
  • 12
  • 2
    +1: very old school and addictive game. ;) – freakish Oct 27 '11 at 21:21
  • Simple. save(); translate(-camera.x, -camera.y); drawItAll(); restore(); –  Oct 13 '15 at 16:53
  • Related: [HTML5 Canvas camera/viewport - how to actually do it?](https://stackoverflow.com/questions/16919601/html5-canvas-camera-viewport-how-to-actually-do-it) – ggorlen Dec 25 '22 at 03:24

2 Answers2

26

So you want a 500x500 canvas to display something (a game level) that is really 9000x500 or so.

This is fine. What you do is think of the canvas as a "viewport" for a larger scene. You translate the canvas (or everything else) to the appropriate spot and draw all of the relevant things at that spot.

Here's an example:

http://jsfiddle.net/hKrrY/

Click on the canvas and hold down your left arrow key to see the scene go by while the red player dot stays "still".

As you scroll by the 100x100 canvas, you are seeing objects that are being drawn at once-offscreen locations like (330,50). Translating the canvas brings them into view.


I guess its worth mentioning that this is where making optimizations in a canvas starts to really matter. The example I gave above is a very simplistic way to make a viewport, and as your code progresses you're going to want to think more and more about what it is you're drawing and how much you're drawing. You'll want to avoid drawing objects that are completely offscreen, for instance, and if your game or app has a background that is 9000x500 you probably don't want to be drawing the entire thing every time!

So the concept is the takeaway here, but you'll want to think very hard about how you implement it and how much work you are making the canvas do. If you end up with performance problems in your side-scrolling app, be sure to let us know :)

Simon Sarris
  • 62,212
  • 13
  • 141
  • 171
  • See my other comment. It can be done better nowadays, reducing the pixels to touch with javascript and enableing pwnage mode :-) – user1610743 Feb 22 '14 at 19:16
6

I've always found it's more efficient to wrap your canvas in a div with the width and height of your viewport and the overflow set to hidden then just draw your whole canvas and scroll the div to where you where you want to view.

So the html would be:

<div id='wrapper' style='width: 200px; height: 200px; overflow: hidden;'>
     <canvas width='1000' height='1000'></canvas>
</div>

and the javascript would be something like

function scrollWrapper(x, y){
    var wrapper = document.getElementById('wrapper');  
    wrapper.scrollTop = x;
    wrapper.scrollLeft = y;
}

Then just call the function with the x and y you want to view. you could wrap it in a setTimeout or something if you wanted to move there instead of just jumping there.

hobberwickey
  • 6,118
  • 4
  • 28
  • 29
  • 1
    Drawing the entire canvas has to be slow though. – scottheckel Oct 27 '11 at 21:17
  • 2
    if you just draw the canvas once, then just update what's changing it's good, particularly if you're just keeping track of and not actually drawing whatever's changing offscreen. – hobberwickey Oct 28 '11 at 01:54
  • +1 for this path. To give an impression: my 640*480px canvas movement with getImageData and putImageData on requestAnimationFrame took nearly 80% of a CPU core (i7 4770k Haswell); using this approach i cut it down to about 10%! That's near an order of magnitude and no matter what the fps counter says, it feels MUCH smoother (probably because anti-aliasing from browser-scrolling kicks in. Tutorial: Make a smaller surrounding DIV with overflow:scroll, bind the mousewheel-event to func with e.preventDefault() and use .scrollTop! This makes everything more fun! – user1610743 Feb 22 '14 at 19:13
  • 1
    add overflow:hidden to the wrapper div – kurumkan Sep 27 '16 at 03:53