使用HTML5 Canvas将图像解析为瓷砖集

3

我正在尝试将一个瓷砖集图像(.png类型)解析成一个数组。我有一个测试画布,我首先将它绘制到画布上,然后从中提取图像信息。当我运行这段代码时,它会抛出“未捕获的类型错误”。我能够记录到this.mainTiles为空。有什么想法吗?我还不知道Canvas的所有细节,所以我卡住了。谢谢帮忙!(另外,您可以忽略最后一行,我只是用它来测试 - 但我想说明它不起作用)。

function TileSet() {
    this.tileSheets = [];
    this.mainTiles = [];
    this.tileHeight = 32;
    this.tileWidth = 32;




    this.addSpriteSheet = function (spriteSheetLoc, name) {

        var tileSheet = new Image();
        try {
            tileSheet.src = spriteSheetLoc;
        }
        catch(err) {
            dMode.Log("Invalid TileSheet Src ( TileSet.setSpriteSheet() Failed ); ERR: " +  err);
        }


        tempContext.drawImage(tileSheet, 0, 0);


        var tilesX = tempContext.width / this.tileWidth;
        var tilesY = tempContext.height / this.tileHeight;

        for(var i=0; i<tilesY; i++) {
            for(var j=0; j<tilesX; j++) {
                // Store the image data of each tile in the array.
                this.mainTiles.push(tempContext.getImageData(j*this.tileWidth, i*this.tileHeight, this.tileWidth, this.tileHeight));
            }
        }


        context.putImageData(this.mainTiles[0], 5, 5);



    }

编辑:以下是画布等元素的定义:

var tempCanvas = document.getElementById("tempCanvas");
var tempContext = tempCanvas.getContext("2d");

var canvas = document.getElementById("gameCanvas");
var context = canvas.getContext("2d");


var Tiles = new TileSet;
//this is the line it gets stuck at


Tiles.addSpriteSheet("resources/tiles/tilea2.png");

编辑2:在markE的回答之后,这里是最新的更新。仍然觉得我缺少关于 .onload 的一个基本属性。

function TileSet() {
    this.Tiles = [];
    this.tileHeight = 32;
    this.tileWidth = 32;
    this.tileCount = 4;




    this.addSpriteSheet = function (spriteSheetLoc, name) {

        var tileSheet = new Image();
        tileSheet.onload = function() {
            tempCanvas.width = tileSheet.width;
            tempCanvas.height = tileSheet.height;
            tempContext.drawImage(tileSheet, 0, 0);



           for (var t=0;t<this.tileCount;t++) {
       this.Tiles[t]=tempContext.getImageData(t*this.tileWidth,t*32,this.tileWidth,tileSheet.height);
               dMode.Log(this.Tiles);
           }


            context.putImageData(this.Tiles[this.Tiles.length-1],0,0);
            dMode.Log(this.Tiles);
        }

        tileSheet.src = spriteSheetLoc;
}

1
你能展示一下 tempContext 是如何定义的吗? - Saulo Silva
没问题。请查看代码的编辑。 - Taylor Hill
1
尝试将 tempContext.widthtempContext.height 分别替换为 tempCanvas.widthtempCanvas.height。我不认为上下文具有宽度/高度。 - Saulo Silva
搞定了!我没意识到我应该直接使用画布来访问它的宽度——我以为我必须使用上下文。非常感谢。 - Taylor Hill
很高兴它起作用了!如果您不介意,我会将我的评论发布为答案,如果您接受它,我会很感激。 - Saulo Silva
当然。这是我第一次使用stackoverflow,所以我会弄清楚的! - Taylor Hill
2个回答

3

以下是将精灵表解析为单独画布图像数据数组的方法

我有一些已经能够实现您想要做的代码。

由于我没有您提供的“resources/tiles/tilea2.png”,所以我使用了自己的“monstersArun.png”,它是一个10行x64列的64x64像素的精灵表。

您可以修改此代码以适应您的精灵表布局(行数x列数和图块大小)。

以下是代码:

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    var tileCount=10;
    var tiles=new Array();
    var tileWidth;
    var t;
    var img=new Image();
    img.onload=function(){
      canvas.width=img.width;
      canvas.height=img.height;
      tileWidth=img.width/tileCount;
      ctx.drawImage(img,0,0);

      for(var t=0;t<tileCount;t++){
            tiles[t]=ctx.getImageData(t*tileWidth,0,tileWidth,img.height);
      }

      // just a test
      // draw the last tiles[] into another canvas
      var canvasTile=document.getElementById("canvasTile");
      var ctxTile=canvasTile.getContext("2d");
      canvasTile.width=tileWidth;
      canvasTile.height=canvas.height;
      ctxTile.putImageData(tiles[tiles.length-1],0,0);
    }
    img.src="monsterarun.png";

}); // end $(function(){});
</script>

</head>

<body>

    <canvas id="canvas" width=300 height=300></canvas><br/>
    <canvas id="canvasTile" width=64 height=64></canvas>

</body>
</html>

[编辑--包括Juan Mendes的好建议解决编码问题]

另外,当我查看您的代码时...在这里:

tileSheet.src = spriteSheetLoc;

这会导致你的图片开始加载。由于加载需要时间,因此javascript开始加载图片,但它也立即转到下一行代码。结果是,在图像可用之前,您下面的代码尝试使用该图像--不好!
因此,在处理其余代码之前,应该给javascript充分加载图像的机会。您可以使用Image的onload方法来实现这一点,如下所示:
var tileSheet = new Image();
tileSheet.onload=function(){

    // put ALL you code that depends on the image being fully loaded here

}
tileSheet.src = spriteSheetLoc;

注意在onload函数之后放置tileSheet.src。实际上,JavaScript会先执行tilesheet.src,然后“回头”执行onload块中的所有代码!

[再次编辑 - 完整代码]

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var tempCanvas = document.getElementById("tempCanvas");
    var tempContext = tempCanvas.getContext("2d");

    var canvas = document.getElementById("gameCanvas");
    var context = canvas.getContext("2d");


    // create a new TileSet
    var Tiles = new TileSet();

    // parse a spritesheet into tiles
    //Tiles.addSpriteSheet("resources/tiles/tilea2.png","anyName");
    Tiles.addSpriteSheet("houseIcon.png","anyName");


    function TileSet() {
        this.Tiles = [];
        this.tileHeight = 32;
        this.tileWidth = 32;
        this.tileCount = 4;

        this.addSpriteSheet = function (spriteSheetLoc, name) {

            var me=this;  // me==this==TileSet

            var tileSheet = new Image();
            tileSheet.onload = function() {

                // calculate the rows/cols in the spritesheet
                // tilesX=rows, tilesY=cols
                var tilesX = tileSheet.width / me.tileWidth;
                var tilesY = tileSheet.height / me.tileHeight;

                // set the spritesheet canvas to spritesheet.png size
                // then draw spritesheet.png into the canvas
                tempCanvas.width = tileSheet.width;
                tempCanvas.height = tileSheet.height;
                tempContext.drawImage(tileSheet, 0, 0);
                for(var i=0; i<tilesY; i++) {

                    for(var j=0; j<tilesX; j++) {

                        // Store the image data of each tile in the array.
                        me.Tiles.push(tempContext.getImageData(j*me.tileWidth, i*me.tileHeight, me.tileWidth, me.tileHeight));
                    }
                }

                // this is just a test
                // display the last tile in a canvas
                context.putImageData(me.Tiles[me.Tiles.length-1],0,0);

            }
            // load the spritesheet .png into tileSheet Image()
            tileSheet.src = spriteSheetLoc;
        }
    }

}); // end $(function(){});
</script>

</head>

<body>
    <canvas id="tempCanvas" width=300 height=300></canvas><br/>
    <canvas id="gameCanvas" width=300 height=300></canvas>
</body>
</html>

1
看起来应该可以工作,但最好解释一下原帖中的代码有什么问题。 - Ruan Mendes
@Juan Mendes:感谢你的推动,我通常更乐于助人...我在我的回答中添加了对OP的帮助 :) - markE
这肯定是一个更干净的解决方案,而且很有道理 - 但出于某种原因,它对我来说只是不起作用。抱歉让你被问题淹没了,我觉得关于 .onload 函数有一些基本的东西我需要搞清楚。我已经将最新版本发布到原始帖子中。 - Taylor Hill
@Taylor Hill,这是您的代码正常工作的版本。几个注意点:在tileSheet.onload中,“this”指的是tileSheet Image()——而不是您的TileSet对象(这就是var me = this;的用途)。您的TileSet对象依赖于在对象之外定义tempContext。相反,您将想要像这样在TileSet内部创建canvas:var myTempCanvas = document.createElement(“canvas”);然后为myTempCanvas创建一个上下文。 - markE
非常感谢您的帮助!我很惊讶这个社区是如此乐于助人。 - Taylor Hill

1

尝试将 tempContext.widthtempContext.height 分别替换为 tempCanvas.widthtempCanvas.height。我认为上下文没有宽度/高度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接