HTML5 drawing artwork on canvas using a for loop?

So, I want to create a minecraft website theme using HTML5. I'm a little shaky in HTML5 / Javascript (didn't use it at the time) and I need help. I am trying to calculate the number of 16x16px tiles that can fit on the screen. Then randomly “generate a map” for the background of the screen. Then I use the same 2 for the loops that generate the map to fill the screen with tiles (which are assigned image paths during the generation process). The problem is that the canvas is completely white. Can someone pick a problem and give me any advice if possible? Thanks in advance! Here is my HTML5 code:

<!DOCTYPE html> <html> <head> <title>Minecraft Background Check</title> </head> <body> <canvas id="bg" style="position:fixed; top:0; left:0; border:1px solid #c3c3c3; width: 100%; height: 100%;"></canvas> <script type="text/javascript"> "use strict"; var c = document.getElementById("bg"); var ctx = c.getContext("2d"); ctx.canvas.width = window.innerWidth; ctx.canvas.height = window.innerHeight; var width = Math.ceil(window.innerWidth / 16); var height = Math.ceil(window.innerHeight / 16); for (var x=0;x<width;x++) { for(var y=0;y<height;y++) { var rand = Math.floor(Math.random()*11); var texLoc = getImageNameFromRand(rand,y,height); var img=new Image(); img.onload = function(){ return function() { ctx.drawImage(img,x*16,y*16); }; }; img.src=texLoc; } } function getImageNameFromRand(rand,yVal,maxY) { var dirt = 'dirt.png'; var stone = 'stone.png'; var cobble = 'cobble.png'; var mosscobble = 'mosscobble.png'; var bedrock = 'bedrock.png'; if(yVal===0) { return dirt; } else if(yVal<3) { if(rand < 7) { return dirt; } else { return stone; } } else if(yVal<5) { if(rand < 4) { return dirt; } else { return stone; } } else if(yVal<maxY-2) { if(rand === 0) { return dirt; } else if(rand < 4) { return cobble; } else if(rand < 5) { return mosscobble; } else { return stone; } } else if(yVal<maxY-1) { if(rand < 4) { return bedrock; } else { return stone; } } else if(yVal<maxY) { if(rand < 7) { return bedrock; } else { return stone; } } else { return bedrock; } return bedrock; } </script> </body> </html> 
+4
source share
3 answers

There is much to explain. I checked the code below and got it working, but I just used one image again and again. We hope this works when combined with your code. Put the code below, instead of your for loops (i.e. after var height and before the getImageNameFromRand function).

Firstly, your code defines all vars in the global namespace, so img var is replaced every time through the source loop, as well as its src and onload function, etc. In addition, x and y, which increase the for loops, get a link through a closure in the onload function, but since they only increase during the outer loop (not in the onload function), they are both set to their final values ​​during the onload call (final values when starting the source loop).

Also, try adding all of your script to an anonymous function, for example (function () {YOUR CODE HERE}) (). This way you will not add to the global namespace with local vars, and onload will have what it needs because of the closure. I tested everything in an anonymous function and it worked for me.

Hope I copied and put it all right. Please comment if it is turned off. Good luck !!!

 //Copy this code in place of your original "for" loop: var imgs = []; var imgIndex = 0; for (var x = 0; x < width; x++){ for(var y = 0; y < height; y++){ var rand = Math.floor(Math.random() * 11); var texLoc = getImageNameFromRand(rand, y, height); imgs[imgIndex] = new Image(); imgs[imgIndex].onload = (function () { var thisX = x * 16; var thisY = y * 16; return function () { ctx.drawImage(this, thisX, thisY); }; }()); imgs[imgIndex].src = texLoc; imgIndex += 1; } } 
+2
source

When you do the following:

 img.onload = function(){ return function() { ctx.drawImage(img,x*16,y*16); }; }; 

You are trying to create a closure over img so that inside the onload handler it refers to the image you created in this loop iteration.

But you do not quite do it. Instead, the onload handler simply defines an anonymous function and then does nothing.

 img.onload = function(img){ return function() { ctx.drawImage(img,x*16,y*16); }; }(img); 

If you change it so that you immediately call the anonymous function and pass it to img , you will close img (from the context of the for loop) and it will do what you want.

If an external anonymous function is not called immediately, it will become the onload handler for the image. And nothing will happen. Turning into an empty canvas.

+1
source

The problem is that in the for loop you draw the image again and again on top of each other. If you exit forloops completely after the first run (x = 0, y = 0), you will see a single tile of your random image at position 0,0. When it goes through the entire double for loop, your last image is drawn in the lower corner of the canvas, which creates the "blank white canvas" that you see. You need him to draw a “pre-filled” canvas of random fragments. It also means that your for loops should be inside the onload () function, because your background only gets it once, not hundreds / thousands of times.

Look for the html5 canvas / tile patterns, then work from there.

also, your onload function does not need an internal function, you can have it something like this:

 img.onload = function(){ // - nested for loops // -> generate a tile pattern to fit the entire canvas // - ctx.drawImage() }; 

Good luck

0
source

Source: https://habr.com/ru/post/1400123/


All Articles