{"id":684,"date":"2024-02-02T16:47:37","date_gmt":"2024-02-02T11:17:37","guid":{"rendered":"http:\/\/codetheory.in\/?p=684---10dd68f6-8be3-49a7-a428-9bbb2d38abeb"},"modified":"2024-02-02T16:47:37","modified_gmt":"2024-02-02T11:17:37","slug":"moving-scrolling-sliding-background-in-html5-canvas","status":"publish","type":"post","link":"https:\/\/codetheory.in\/moving-scrolling-sliding-background-in-html5-canvas\/","title":{"rendered":"Moving\/Scrolling\/Sliding Background in HTML5 Canvas"},"content":{"rendered":"

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!<\/p>\n

<\/p>\n

Problem<\/h2>\n

It is important to understand the problem first. A quick demo (refresh it):<\/p>\n

<\/pre>\n

The code ain’t something you haven’t seen before.<\/p>\n

\r\n\/\/ Velocity X\r\nvar vx = 0;\r\n\r\nvar img = new Image();\r\nimg.src = 'http:\/\/cssdeck.com\/uploads\/media\/items\/4\/4OIJyak.png';\r\n\r\n(function renderGame() {\r\n\twindow.requestAnimationFrame(renderGame);\r\n\t\r\n\tctx.clearRect(0, 0, W, H);\r\n\t\r\n\tctx.fillStyle = '#333';\r\n\tctx.fillRect(0, 0, 500, 400);\r\n\t\r\n\tctx.drawImage(img, vx, 50);\r\n\t\r\n\tvx -= 2;\r\n}());\r\n<\/pre>\n

A quick fillRect<\/code> followed by a drawImage<\/code>. Note, the second argument passed to drawImage<\/code> is a variable we define called vx<\/code> 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;<\/code>.<\/p>\n

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.<\/p>\n

Solution<\/h2>\n

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

On trying harder, I found an interesting solution. Basically we need to drawImage()<\/code> twice.<\/p>\n

<\/pre>\n

So the code that does the trick is this:<\/p>\n

\r\nctx.drawImage(img, vx, 50);\r\nctx.drawImage(img, img.width-Math.abs(vx), 50);\r\n\r\nif (Math.abs(vx) > img.width) {\r\n\tvx = 0;\r\n}\r\n\r\nvx -= 2;\r\n<\/pre>\n

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<\/em> 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)<\/code>.<\/p>\n

As soon as our velocity is more than the image width, we re-set it to 0.<\/p>\n

\r\nif (Math.abs(vx) > img.width) {\r\n\tvx = 0;\r\n}\r\n<\/pre>\n

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.<\/p>\n

Note:<\/strong> 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.<\/p>\n","protected":false},"excerpt":{"rendered":"

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 … Continue reading “Moving\/Scrolling\/Sliding Background in HTML5 Canvas”<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[40],"tags":[14,15,8],"_links":{"self":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts\/684"}],"collection":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/comments?post=684"}],"version-history":[{"count":17,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts\/684\/revisions"}],"predecessor-version":[{"id":184690,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts\/684\/revisions\/184690"}],"wp:attachment":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/media?parent=684"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/categories?post=684"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/tags?post=684"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}