requestAnimationFrame与for循环绘图问题

4
我正在制作一个俄罗斯方块游戏,尝试使用requestAnimationFrame在黑色背景上绘制我的T形方块。但是问题是,requestAnimationFrame会绘制两次方块,然后停止绘制,即使for循环仍在运行。也就是说,只有两次后,我才看到黑色背景。当我注释掉黑色背景时,方块可以正常显示/动画。我真的不知道为什么会出现这种情况。
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

const T = [
        [
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [0, 1, 1, 1, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0]
        ],

       [
            [0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 1, 1, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0]
        ],

       [
            [0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 1, 1, 1, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0]
        ],

        [
            [0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 1, 1, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0]
        ],
    ]

    var piece = T[0];

    const player = {
        position: {x: 5, y: -1},
        piece: piece,
    }

function colorPiece(piece, offset) {
    for(y = 0; y < piece.length; y++) {
        for(x = 0; x < piece.length; x++) {
            if (piece[y][x] !== 0) {
                ctx.fillStyle = "red";
                ctx.fillRect(x + offset.x, y + offset.y, 1, 1);
            }
        }
    }
}

function drawCanvas() {
    ctx.fillStyle = "black";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.scale(20, 20);
    colorPiece(player.piece, player.position);
}

function update() {
        drawCanvas();
        requestAnimationFrame(update);
}

update();

请问您能否编辑您的问题,以便展示创建ctx的代码?我在这段代码中没有看到它。谢谢!此外,请解释一下“停止绘制”与“动画”的区别——我在这段代码中没有看到任何可以使图形移动的东西。您是指在两帧之后只会得到一个黑色矩形吗? - cxw
2
我的强迫症让我指出,“piece”应该是拼写为“I-E”,而不是“E-I”……只要你不在代码的一半纠正拼写,那么你就没问题。 - Patrick Roberts
Patrick,你的评论让我笑了。抱歉。我想我至少一直拼错了它。 - user4586547
是的,cxw,在红色t形状迭代两次之后,我只看到黑色背景。 - user4586547
是的,我仍在学习有关清晰代码的知识。我尝试了T [0],但出现了相同的问题。 - user4586547
1个回答

1

好的 - 工作版本,带有一个fiddle 这里。有一些更改。最大的更改是:

  • Don't use canvas.scale(), since it's cumulative per this (see "More Examples"). Instead, use 20*x and 20*y for blocks 20x20.

    Edit Based on a further test, it looks like this was the most significant change.

  • Rename so that piece is not used as all of a variable, a field name in player, and a parameter of colorPiece

  • Move the ctx creation into update() (now called doUpdate()) per this fiddle example. Pass ctx as a parameter to other functions.

  • Move the red fillStyle assignment out of the loop, since you only need to do it once, and then you can draw all the rectangles without changing it.

  • In the loops:

    for(var y = 0; y < thePiece.length; ++y) {
        for(var x = 0; x < thePiece[y].length; ++x) { ... } }
    

    Keep x and y in the local scope, using var.

    When you are ready to go across a row, that's thePiece[y].length, i.e., the length of the row. Using thePiece.length there would have broken for non-square elements of T.

  • Added a <p id="log"/> and a javascript framenum so that I could see that doUpdate() was indeed being called.

如果您尚未这样做,请确保在测试时打开控制台,以便查看错误消息。如果drawCanvas导致错误,它将阻止再次调用requestAnimationFrame。我认为这就是为什么我上面链接的那个 fiddle在帧绘制函数的开始而不是结束处调用requestAnimationFrame的原因。
希望能有所帮助!

代码

const T = [
        [
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [0, 1, 1, 1, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0]
        ],

       [
            [0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 1, 1, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0]
        ],

       [
            [0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 1, 1, 1, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0]
        ],

        [
            [0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 1, 1, 0],
            [0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0]
        ],
    ]

    const player = {
        position: {x: 5, y: -1},
        piece: T[0]
    }

function colorPiece(ctx, thePiece, offset) {
    ctx.fillStyle = "red";
    for(var y = 0; y < thePiece.length; ++y) {
        for(var x = 0; x < thePiece[y].length; ++x) {
            if (thePiece[y][x] !== 0) {
                ctx.fillRect(20*(x + offset.x), 20*(y + offset.y), 20, 20);
            }
        }
    }
}

function drawCanvas(ctx) {
    ctx.fillStyle = "#000000";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    colorPiece(ctx, player.piece, player.position);
}

var framenum=0;

function doUpdate(timestamp) {
        document.getElementById("log").innerHTML = framenum.toString();
        ++framenum;
        var canvas = document.getElementById("canvas");
        var ctx = canvas.getContext("2d");

        drawCanvas(ctx);
        window.requestAnimationFrame(doUpdate);
}

doUpdate();

是的!我甚至没有想到fillStyle需要在fillRect之前进行设置。根据您的建议,我使用调试器逐步调整了我的代码,当我将fillStyle放在fillRect之前时,问题得到了解决。非常感谢您的帮助!!! - user4586547

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