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
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 ); } }
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 boardclass 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; } }
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/columnf/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 pieceshape), 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; } }
Piece.row/Piece. col). You have to multiply this by PIXEL_SIZE and add the separation with the box.
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!