Home >Web Front-end >JS Tutorial >Creating a Tetris with JavaScript IV: canvas

Creating a Tetris with JavaScript IV: canvas

PHPz
PHPzOriginal
2024-07-16 20:45:22415browse

Introduction

In this new installment of the series, we will see how to display the board and the piece that is currently going down on the screen. To do this, we will have to draw it in the browser, and the option we have to do so is the Canvas element of HTML.

class Canvas {
    static SEPARATION = 2;
    #_painting = false;
    #_element = null;
    #_board = null;
    #_piece = null;

    constructor(element, board)
    {        
        element.width = 5 + ( board.cols * Board.PIXEL_SIZE );
        element.height = 5 + ( board.rows * Board.PIXEL_SIZE );

        this._board = board;
        this._element = element;
    }

    // más cosas...
}

This class Canvas represents the HTML element of the same name, which is passed as a parameter in the constructor. Since you are going to draw the board, it is also passed as a parameter, in order to access the points to draw.

The first thing it does is size the Canvas element to be able to accommodate the board, according to the dimensions that the board itself reports through its cols and rows. The board also tells us how many pixels makes a point of each piece or each cell of the board, through PIXEL_SIZE.

Redrawing the game

Let's stop detours. We have to paint the board and the piece that is descending at that moment, right? Well let's get down to business.


class Canvas {
    // más cosas...

    paint()
    {
        if ( this._painting ) {
            return;
        }

        const ctx = this.element.getContext( "2d" );
        const SEP = Canvas.SEPARATION;

        this._painting = true;
        this.clear();

        this.paintBoard( ctx, SEP );
        this.paintPiece( ctx, SEP );

        this._painting = false;
    }

    clear()
    {
        const ctx = this.element.getContext( "2d" );

        ctx.clearRect( 0, 0, this.element.width, this.element.height );
    }
}
First we take the context for 2D, which will allow us to draw on the canvas. Out of curiosity, there is also a context for 3D, which is based on WebGL.

We have guards (

_painting), which prevent several threads from executing the method at the same time (at different points), at a given time. This could happen if the method was executed for longer than the time between redraws. Although well, in that case we would have many other problems...

The next step is to delete what was on the screen in the previous redraw (

frame). We do this with the clear() method, which uses clearRect() to delete the image on the canvas.

And then we paint the board, and then the piece that comes down at that moment. Well, that would be it. Ale, delivery completed.

Noooo. Let's see how the board and the piece are painted. The first thing is to paint the board. SEP is the separation that we will leave between the pieces and the board square. This box is the first thing we draw in the code paragraph titled

Draw frame. It is a simple rectangle that can be drawn with strokeRect(), which accepts four parameters with the position of the upper left vertex, and then its width and height.

Painting the board

class Canvas {
    // más cosas...

    paintBoard(ctx, SEP)
    {       
        // Draw frame
        ctx.strokeWidth = 1;
        ctx.strokeStyle = this.board.color;
        ctx.strokeRect( 1, 1,
                  this.element.width - 1,
                  this.element.height  -1 );

        // Draw board
        for(let numRow = 0; numRow < this.board.rows; ++numRow)
        {
            const row = this.board.getRow( numRow );

            for(let numCol = 0; numCol < row.length; ++numCol) {
                if ( Boolean( row[ numCol ] ) ) {
                    ctx.strokeWidth = 1;
                    ctx.strokeStyle = this.board.color;
                    ctx.fillStyle = this.board.color;
                    ctx.fillRect(
                        SEP + ( Board.PIXEL_SIZE * numCol ),
                        SEP + ( Board.PIXEL_SIZE * numRow ),
                        Board.PIXEL_SIZE,
                        Board.PIXEL_SIZE );
                }
            }            
        }

        return;
    }
}
Next comes a nested loop (rows and columns), so we'll see which of the cells on the board have content (an integer 1, versus an integer 0), and then draw a small square with side PIXEL_SIZE.

So, the first loop goes through the rows until

Board.rows. We then obtain the complete row with the getRow() method, to traverse it with the inner loop, until Board.cols.

So, given a cell in row/column

f/c, Board.getCell(f, c), and taking into account that JavaScript has a constructor for Boolean that accepts an integer that with any value except 0, means true, we paint a square with side PIXEL_SIZE. So, to know where to paint the row f, we have to multiply by PIXEL_SIZE and add the separation between the board box and the first cell. Since they are square, we will find the column c in the same way: SEP + (c * PIXEL_SIZE).

Painting the piece

We do something similar with the pieces. By having a shape (

shape), which is nothing more than a matrix, we will again have two loops, the outer one for rows and the inner one for columns.

class Canvas {
    // más cosas...

    paintPiece(ctx, SEP)
    {
        const SHAPE = this.piece.shape;

        for(let numRow = 0; numRow < SHAPE.length; ++numRow) {
            const ROW = SHAPE[ numRow ];
            for(let numCol = 0; numCol < ROW.length; ++numCol) {
                if ( Boolean( ROW[ numCol ] ) ) {
                    ctx.strokeWidth = 1;
                    ctx.strokeStyle = this.piece.color;
                    ctx.fillStyle = this.piece.color;
                    ctx.fillRect(
                        SEP
                        + ( this.piece.col * Board.PIXEL_SIZE )
                        + ( numCol * Board.PIXEL_SIZE ),
                        SEP + 
                        + ( this.piece.row * Board.PIXEL_SIZE )
                        + ( numRow * Board.PIXEL_SIZE ),
                        Board.PIXEL_SIZE,
                        Board.PIXEL_SIZE );
                }
            }
        }

        return;
    }
}
Again, if we find a 1, we will paint a square with a PIXEL_SIZE side. The position to paint each square that makes up the piece is given by the position of the row/column piece itself (

Piece.row/Piece. col). You have to multiply this by PIXEL_SIZE and add the separation with the box.

El juego Insertrix en su estado actual

Right now, what we can see is quite... bland. The board is empty, and we don't have a game loop, so the pieces don't even go down. We will discuss that topic in the next installment, so that we can begin to see something similar to the image above.

The above is the detailed content of Creating a Tetris with JavaScript IV: canvas. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Previous article:Days of Code Week 3Next article:Days of Code Week 3