HTML5 рисования картинок на холсте с помощью цикла? - PullRequest
4 голосов
/ 07 марта 2012

Итак, я хочу создать тему сайта для майнкрафта с использованием HTML5. Я немного шаткий в HTML5 / Javascript (я давно его не использовал), и мне нужна помощь. Я пытаюсь рассчитать количество плиток размером 16x16 пикселей, которые могут уместиться на экране. Затем случайным образом «сгенерируйте карту» для фона экрана. Затем я использую те же 2 для циклов, которые генерируют карту, чтобы заполнить экран плитками (которым назначаются пути изображения в процессе генерации). Проблема в том, что холст полностью белый. Кто-нибудь может выбрать проблему и дать мне какие-нибудь советы, если это возможно? Заранее спасибо! Вот мой код HTML5:

<!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>

Ответы [ 3 ]

3 голосов
/ 07 марта 2012

Здесь много чего можно объяснить. Я протестировал приведенный ниже код и заставил его работать, но я просто использовал одно изображение снова и снова. Надеюсь, это сработает при слиянии с вашим кодом. Поместите код ниже вместо ваших циклов for (т.е. после var var и перед функцией getImageNameFromRand).

Во-первых, ваш код определяет все переменные в глобальном пространстве имен, поэтому img var будет заменяться каждый раз в исходном цикле вместе с его функциями src и onload и т. Д. Кроме того, на x и y, которые увеличивают циклы for, ссылаются через замыкание в функции onload, но поскольку они увеличиваются только во внешнем цикле (не в функции onload), им обоим присваиваются свои конечные значения во время вызова onload ( конечные значения при запуске исходного цикла).

Также попробуйте поместить весь ваш скрипт в анонимную функцию, например (function () {ВАШ КОД ЗДЕСЬ}) (). Таким образом, вы не будете добавлять в глобальное пространство имен локальные переменные, и onload будет иметь то, что ему нужно из-за закрытия. Я тестировал помещение всего в анонимную функцию, и это работало на меня.

Надеюсь, я все это правильно скопировал и вставил. Пожалуйста, прокомментируйте, если он выключен. Удачи !!!

//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;
    }
}
1 голос
/ 07 марта 2012

Когда вы делаете следующее:

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

Вы пытаетесь создать замыкание над img, чтобы внутри обработчика onload оно ссылалось на изображение, которое вы создали в этой итерациицикла.

Но вы не совсем это делаете.Вместо этого обработчик onload просто определяет анонимную функцию, а затем ничего не делает.

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

Если вы измените ее так, что вы немедленно вызываете анонимную функцию и передаете img, тогда вы будетезакройте img (из контекста цикла for), и он будет делать то, что вы хотите.

Если внешняя анонимная функция вызывается не сразу, то она станет обработчиком загрузки для изображения.И ничего не случится.Ведущий на пустой холст.

0 голосов
/ 07 марта 2012

Проблема в том, что в цикле for вы рисуете изображение снова и снова поверх друг друга.Если после первого запуска вы полностью вырвитесь из петель (x = 0, y = 0), вы увидите одну плитку случайного изображения в позиции 0,0.Когда он проходит весь цикл double for, ваше последнее изображение рисуется в нижнем углу холста, что создает «чистый белый холст», который вы видите.Вы должны нарисовать «предварительно заполненный» холст случайных плиток.Это также означает, что ваши циклы for должны быть внутри функции onload (), потому что ваш фон рисуется только один раз, а не сотни / тысячи раз.

Найдите html5-шаблоны холста / плитки, а затем работайте оттуда.

Кроме того, вашей функции загрузки не нужна внутренняя функция, вы можете получить ее примерно так:

img.onload = function(){

    // - nested for loops
    //   -> generate a tile pattern to fit the entire canvas

    // - ctx.drawImage()

};

Удачи!

...