{"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
It is important to understand the problem first. A quick demo (refresh it):<\/p>\n
<\/pre>\nThe 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>\nA 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>\nSo 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>\nWe 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>\nThis 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}]}}