Saturday 8 July 2023

Web Tutorial: Tetris in vanilla JavaScript (Part 2/4)

This is the bread and butter of the Tetris game - blocks! There are a few different kinds of blocks in the game, and each of them can be rotated four ways. To represent that, let's create the shapes array in the game object.
<script>
    var game = {
        paused: false,
        stopped: false,
        shapes:
        [
        
        ]

    }
</script>


There are five different shapes, so let's have five elements in this array. Each of these are, in turn, arrays.
shapes:
[
    [
            
    ],
    [
    
    ],
    [
    
    ],
    [
    
    ],
    [
    
    ]

]


Each of these shapes have four rotations. So give them four elements. Again, these are also arrays.
shapes:
[
    [
        [
        
        ],
        [
        
        ],
        [
        
        ],
        [
        
        ]

    ],
    [
        [
        
        ],
        [
        
        ],
        [
        
        ],
        [
        
        ]

    ],
    [
        [
        
        ],
        [
        
        ],
        [
        
        ],
        [
        
        ]

    ],
    [
        [
        
        ],
        [
        
        ],
        [
        
        ],
        [
        
        ]

    ],
    [
        [
        
        ],
        [
        
        ],
        [
        
        ],
        [
        
        ]

    ]
]


Each rotation is a 4 by 4 grid, so let's have 4 elements - each element is an array of 4 elements, all 0s for now.
shapes:
[
    [
        [
            [0, 0, 0, 0],
            [0, 0, 0, 0],
            [0, 0, 0, 0],
            [0, 0, 0, 0],

        ],
        [
        
        ],
        [
        
        ],
        [
        
        ]
    ],
    [
        [
        
        ],
        [
        
        ],
        [
        
        ],
        [
        
        ]
    ],
    [
        [
        
        ],
        [
        
        ],
        [
        
        ],
        [
        
        ]
    ],
    [
        [
        
        ],
        [
        
        ],
        [
        
        ],
        [
        
        ]
    ],
    [
        [
        
        ],
        [
        
        ],
        [
        
        ],
        [
        
        ]
    ]
]


Now let's say we want to create a square block. We replace the 0s in this "matrix" with 1s where appropriate. The way I have written this code, it's easy to discern the shape.
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 1, 1, 0]


What about the rest? Well, this is easy, because the square block looks the same no matter what way you rotate it! So just repeat for the other array element.
[
    [
        [0, 0, 0, 0],
        [0, 0, 0, 0],
        [0, 1, 1, 0],
        [0, 1, 1, 0]
    ],
    [
        [0, 0, 0, 0],
        [0, 0, 0, 0],
        [0, 1, 1, 0],
        [0, 1, 1, 0]

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

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

    ]
],


The next one we will do is the long straight block. It has only two different views - either horizontally or vertically.
[
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [1, 1, 1, 1]

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

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

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

]


Here is the completed shapes property, with all the shapes and their possible rotations.
shapes:
[
    [
        [
            [0, 0, 0, 0],
            [0, 0, 0, 0],
            [0, 1, 1, 0],
            [0, 1, 1, 0]
        ],
        [
            [0, 0, 0, 0],
            [0, 0, 0, 0],
            [0, 1, 1, 0],
            [0, 1, 1, 0]
        ],
        [
            [0, 0, 0, 0],
            [0, 0, 0, 0],
            [0, 1, 1, 0],
            [0, 1, 1, 0]
        ],
        [
            [0, 0, 0, 0],
            [0, 0, 0, 0],
            [0, 1, 1, 0],
            [0, 1, 1, 0]
        ]
    ],
    [
        [
            [0, 0, 0, 0],
            [0, 0, 0, 0],
            [0, 0, 0, 0],
            [1, 1, 1, 1]
        ],
        [
            [0, 1, 0, 0],
            [0, 1, 0, 0],
            [0, 1, 0, 0],
            [0, 1, 0, 0]
        ],
        [
            [0, 0, 0, 0],
            [0, 0, 0, 0],
            [0, 0, 0, 0],
            [1, 1, 1, 1]
        ],
        [
            [0, 1, 0, 0],
            [0, 1, 0, 0],
            [0, 1, 0, 0],
            [0, 1, 0, 0]
        ]
    ],
    [
        [
            [0, 0, 0, 0],
            [0, 0, 0, 0],
            [1, 1, 0, 0],
            [0, 1, 1, 0]

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

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

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

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

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

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

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

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

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

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

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

        ]
    ]
]


The block shapes have been defined, so we will need to fill in the grid. But before that, there's a few things we need to do. We need to define some new properties.
grid - an empty array by default, which will be used to populate the grid on screen.
colors - an array of five different colors.
currentBlock - an object, but null by default.
nextBlock - an object, but null by default.
var game = {
    grid: [],
    colors: ["red", "purple", "cyan", "yellow", "lime"],
    currentBlock: null,
    nextBlock: null,

    paused: false,
    stopped: false,


In the reset() method, we first give the grid array its shape. We begin by defining it as an empty array. Now, it's going to be  an array of 20 elements, each of which is another array of 10 elements. So for now, use a For loop to iterate from 0 to 19.
reset: function()
{
    this.grid = [];
    for(var i = 0; i < 20; i++)
    {

    }


    this.renderGrid();
},


Then inside the loop, we define temp as an empty array. We then have a nested For loop iterating from 0 to 9, and within it, we push the value null into temp. So we'll have an array of 10 null values!
reset: function()
{
    this.grid = [];
    for(var i = 0; i < 20; i++)
    {
        var temp = [];
        for(var j = 0; j < 10; j++)
        {
            temp.push(null);
        }

    }

    this.renderGrid();
},


Once done, we will push temp into grid. At the end of it, we will have a 20 by 10 elements array.
reset: function()
{
    this.grid = [];
    for(var i = 0; i < 20; i++)
    {
        var temp = [];
        for(var j = 0; j < 10; j++)
        {
            temp.push(null);
        }

        this.grid.push(temp);
    }

    this.renderGrid();
},


We set currentBlock to the object returned by calling getRandomBlock(). We do the same for nextBlock.
reset: function()
{
    this.grid = [];
    for(var i = 0; i < 20; i++)
    {
        var temp = [];
        for(var j = 0; j < 10; j++)
        {
            temp.push(null);
        }

        this.grid.push(temp);
    }

    this.currentBlock = this.getRandomBlock();
    this.nextBlock = this.getRandomBlock();


    this.renderGrid();
},


Time to define getRandomBlock().
renderGrid: function()
{
    var grid = document.getElementById("grid");

    grid.innerHTML = "";

    var pauseScreen = document.createElement("div");
    pauseScreen.id = "pauseScreen";
    grid.appendChild(pauseScreen);

    var stopScreen = document.createElement("div");
    stopScreen.id = "stopScreen";
    grid.appendChild(stopScreen);
},
getRandomBlock: function()
{

},

setPause: function(isAuto)    
{
    if (this.stopped) return;

    this.paused = (this.paused ? false : true);

    if (!isAuto)
    {
        var pauseScreen = document.getElementById("pauseScreen");
        pauseScreen.style.visibility = (this.paused ? "visible" : "hidden");
    }
},


Define shapeIndex and colorIndex. Using a JavaScript random number generator, we make shapeIndex any number from 0 to the number of shapes, minus 1. And colorIndex will be a number from 0 to the number of colors, minus 1.
getRandomBlock: function()
{
    var shapeIndex = Math.floor(Math.random() * this.shapes.length);
    var colorIndex = Math.floor(Math.random() * this.colors.length);

},


Then we return an object containing two properties. The first is shape, and for its value we pass in shapeIndex. The second is color and it will be slightly different; colorIndex will point to an element inside colors. Thus, in renderGrid(), currentBlock and nextBlock will be an object like this.
getRandomBlock: function()
{
    var shapeIndex = Math.floor(Math.random() * this.shapes.length);
    var colorIndex = Math.floor(Math.random() * this.colors.length);

    return {"shape": shapeIndex, "color": this.colors[colorIndex]};
},


The next thing we will do is work with the method populateAspect(). The parameter id is the id of the element we will be populating, So we begin by clearing that element of content.
getRandomBlock: function()
{
    var shapeIndex = Math.floor(Math.random() * this.shapes.length);
    var colorIndex = Math.floor(Math.random() * this.colors.length);

    return {"shape": shapeIndex, "color": this.colors[colorIndex]};
},
populateAspect: function(id, shape, color, className)
{
    var container = document.getElementById(id);
    container.innerHTML = "";                    
},

setPause: function(isAuto)    
{
    if (this.stopped) return;

    this.paused = (this.paused ? false : true);

    if (!isAuto)
    {
        var pauseScreen = document.getElementById("pauseScreen");
        pauseScreen.style.visibility = (this.paused ? "visible" : "hidden");
    }
},


The next parameter is shape, which will be the exact sub-array in shapes that we will be representing. We first iterate through the number of elements in shape.
populateAspect: function(id, shape, color, className)
{
    var container = document.getElementById(id);
    container.innerHTML = "";

    for (var i = 0; i < shape.length; i++)
    {
    
    }  
                 
},


Then we iterate through the number of elements in each element using a nested For.
populateAspect: function(id, shape, color, className)
{
    var container = document.getElementById(id);
    container.innerHTML = "";

    for (var i = 0; i < shape.length; i++)
    {
        for (var j = 0; j < shape[i].length; j++)
        {

        }  
 
    }                    
},


Create a div, square. Set its class to the last parameter, className. If the current element has the value of 1, add the third parameter, color, to the class.
populateAspect: function(id, shape, color, className)
{
    var container = document.getElementById(id);
    container.innerHTML = "";

    for (var i = 0; i < shape.length; i++)
    {
        for (var j = 0; j < shape[i].length; j++)
        {
            var square = document.createElement("div");
            square.className = className + " " + (shape[i][j] == 1 ? color : "");

        }    
    }                    
},


Then we append square to the containing element.
populateAspect: function(id, shape, color, className)
{
    var container = document.getElementById(id);
    container.innerHTML = "";

    for (var i = 0; i < shape.length; i++)
    {
        for (var j = 0; j < shape[i].length; j++)
        {
            var square = document.createElement("div");
            square.className = className + " " + (shape[i][j] == 1 ? color : "");

            container.appendChild(square);
        }    
    }                    
},


We first call this at the tail end of the reset() function. We use this to show the user which block is next. Thus, we pass into populateAspect() these arguments.
Firstly, "gridNext", which is the id of the container we will populate. Secondly, the shape pointed to by the shape property of nextBlock. Since we only want to show the first rotation, we use the first element. Thirdly, the color, which is the color property of nextBlock. Finally, the class, which is square_small.
reset: function()
{
    this.grid = [];
    for(var i = 0; i < 20; i++)
    {
        var temp = [];
        for(var j = 0; j < 10; j++)
        {
            temp.push(null);
        }

        this.grid.push(temp);
    }

    this.currentBlock = this.getRandomBlock();
    this.nextBlock = this.getRandomBlock();

    this.renderGrid();
    this.populateAspect("gridNext", this.shapes[this.nextBlock.shape][0], this.nextBlock.color, "square_small");
},


Before we go any further, we also need to define the square_small CSS class. It's basically a 10 pixel square, floated left.
#grid
{
    width: 200px;
    height: 400px;
    background-color: rgba(0, 0, 0, 0.5);
    margin: 5px auto 5px auto;
    position: relative;
    border: 5px inset rgb(155, 155, 155);
}

.square_small
{
    width: 10px;
    height: 10px;
    float: left;
}


#footer
{
    width: 100%;
    height: 100px;        
    text-align: center;
}


You see that we have a 3 by 3 grid of squares. But there's no block! That's because we have not yet handled colors in the CSS.




We also need to define styles for various colors. For, say, the color yellow, we have the yellow CSS class. We define a pseudoselector to create an inner square.
The border is a solid yellow and the background, a translucent yellow.
.pad button
{
    width: 100%;
    height: 100%;
    background: none;
    color: rgb(255, 255, 255);
    font-size: 1em;
}

.yellow::before
{
    content: "";
    display: block;
    border: 1px solid rgb(255, 255, 0);
    background-color: rgba(255, 255, 0, 0.8);
}


#pauseScreen, #stopScreen
{
    position: absolute;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.8);
    visibility: hidden;
}


The rest of the colors are as follows. Notice we even define white even though it isn't in the colors array. That will come in useful later.
.buttons button
{
    width: 50px;
    height: 50px;
    border-radius: 50%;
    font-size: 3em;
}

.pad button
{
    width: 100%;
    height: 100%;
    background: none;
    color: rgb(255, 255, 255);
    font-size: 1em;
}

.yellow::before
{
    content: "";
    display: block;
    border: 1px solid rgb(255, 255, 0);
    background-color: rgba(255, 255, 0, 0.8);
}

.cyan::before
{
    content: "";
    display: block;
    border: 1px solid rgb(0, 255, 255);
    background-color: rgba(0, 255, 255, 0.8);
}

.lime::before
{
    content: "";
    display: block;
    border: 1px solid rgb(0, 255, 0);
    background-color: rgba(0, 255, 0, 0.8);
}

.purple::before
{
    content: "";
    display: block;
    border: 1px solid rgb(255, 0, 255);
    background-color: rgba(255, 0, 255, 0.8);
}

.red::before
{
    content: "";
    display: block;
    border: 1px solid rgb(255, 0, 0);
    background-color: rgba(255, 0, 0, 0.8);
}

.white::before
{
    content: "";
    display: block;
    border: 1px solid rgb(255, 255, 255);
    background-color: rgba(255, 255, 255, 0.8);
}


.square_small::before
{
    width: 8px;
    height: 8px;
}            

#pauseScreen, #stopScreen
{
    position: absolute;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.8);
    visibility: hidden;
}


And then we apply a pseudselector for square_small, to set height and width to 8 pixels.
.white::before
{
    content: "";
    display: block;
    width: 18px;
    height: 18px;
    border: 1px solid rgb(255, 255, 255);
    background-color: rgba(255, 255, 255, 0.8);
}

.square_small::before
{
    width: 8px;
    height: 8px;
}      
     

#pauseScreen, #stopScreen
{
    position: absolute;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.8);
    visibility: hidden;
}


Reload! You can see what the next block is now.



Looks like you got the gist of what's happening, so we will take care of the moving block now. Add a new property, currentPosition. It is an object with the properties x, y and r, all set to 0 by default. x represents the horizontal position, y the vertical, and r the rotation index.
var game = {
    grid: [],
    colors: ["red", "purple", "cyan", "yellow", "lime"],
    currentBlock: null,
    nextBlock: null,
    currentPosition: { x: 0, y: 0, r: 0},
    paused: false,

    stopped: false,


At the start of the renderGrid() method, set currentPosition to an object. This is the position and rotation the moving block has every time the grid is re-rendered.
renderGrid: function()
{
    this.currentPosition = { x: 0, y: -4, r: 0};

    var grid = document.getElementById("grid");

    grid.innerHTML = "";

    var pauseScreen = document.createElement("div");
    pauseScreen.id = "pauseScreen";
    grid.appendChild(pauseScreen);

    var stopScreen = document.createElement("div");
    stopScreen.id = "stopScreen";
    grid.appendChild(stopScreen);
},


And after clearing grid of all content, we create a div and give it an id of movingBlock.
renderGrid: function()
{
    this.currentPosition = { x: 0, y: -4, r: 0};

    var grid = document.getElementById("grid");

    grid.innerHTML = "";

    var movingBlock = document.createElement("div");
    movingBlock.id = "movingBlock";


    var pauseScreen = document.createElement("div");
    pauseScreen.id = "pauseScreen";
    grid.appendChild(pauseScreen);

    var stopScreen = document.createElement("div");
    stopScreen.id = "stopScreen";
    grid.appendChild(stopScreen);
},


Now append movingBlock to grid, then call positionBlock().
renderGrid: function()
{
    this.currentPosition = { x: 0, y: -4, r: 0};

    var grid = document.getElementById("grid");

    grid.innerHTML = "";

    var movingBlock = document.createElement("div");
    movingBlock.id = "movingBlock";

    grid.appendChild(movingBlock);

    this.positionBlock();


    var pauseScreen = document.createElement("div");
    pauseScreen.id = "pauseScreen";
    grid.appendChild(pauseScreen);

    var stopScreen = document.createElement("div");
    stopScreen.id = "stopScreen";
    grid.appendChild(stopScreen);
},


Let's define positionBlock().
getRandomBlock: function()
{
    var shapeIndex = Math.floor(Math.random() * this.shapes.length);
    var colorIndex = Math.floor(Math.random() * this.colors.length);

    return {"shape": shapeIndex, "color": this.colors[colorIndex]};
},
positionBlock: function()
{

},

setPause: function(isAuto)    
{
    if (this.stopped) return;

    this.paused = (this.paused ? false : true);

    if (!isAuto)
    {
        var pauseScreen = document.getElementById("pauseScreen");
        pauseScreen.style.visibility = (this.paused ? "visible" : "hidden");
    }
},


We declare container and set it to the value of the element movingBlock.
positionBlock: function()
{
    var container = document.getElementById("movingBlock");
},


The set the left and top margins of movingBlock to the x and y properties of the currentPosition object. Since the size of each square is supposed to be 20 pixels (take note!) we will have to multiply each value by 20.
positionBlock: function()
{
    var container = document.getElementById("movingBlock");
    container.style.marginLeft = (this.currentPosition.x * 20) + "px";
    container.style.marginTop = (this.currentPosition.y * 20) + "px";

},


Here is the style for movingBlock. The position property is absolute because it is going to appear anywhere within grid regardless of what other elements are inside. Width and height have been set to 80 pixels because each square is 20 pixels in height and width, and a 4 by 4 square grid will be displayed at any one time. By default, top and left margins are at 0.
.square_small::before
{
    width: 8px;
    height: 8px;
}            

#movingBlock
{
    position: absolute;
    width: 80px;
    height: 80px;
    margin-top: 0px;
    margin-left: 0px;
}


#pauseScreen, #stopScreen
{
    position: absolute;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.8);
    visibility: hidden;
}


See the red square at the top left, right over the Stage and Score panel? That's movingBlock. It's at the top because the y property of currentPosition was set to -4.




Next, create a new div with the id of movingBlockAspects. Then append it to movingBlock before appending movingBlock to grid.
renderGrid: function()
{
    this.currentPosition = { x: 0, y: -4, r: 0};

    var grid = document.getElementById("grid");

    grid.innerHTML = "";

    var movingBlock = document.createElement("div");
    movingBlock.id = "movingBlock";

    var movingBlockAspects = document.createElement("div");
    movingBlockAspects.id = "movingBlockAspects";

    movingBlock.appendChild(movingBlockAspects);

    grid.appendChild(movingBlock);

    this.positionBlock();

    var pauseScreen = document.createElement("div");
    pauseScreen.id = "pauseScreen";
    grid.appendChild(pauseScreen);

    var stopScreen = document.createElement("div");
    stopScreen.id = "stopScreen";
    grid.appendChild(stopScreen);
},


This is the style for movingBlockAspects. It is 4 times the width of its parent, occupies full height, and the left marginis at 0%.
#movingBlock
{
    position: absolute;
    width: 80px;
    height: 80px;
    margin-top: 0px;
    margin-left: 0px;
}

#movingBlockAspects
{
    width: 400%;
    height: 100%;
    margin-left: 0%;
}


#pauseScreen, #stopScreen
{
    position: absolute;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.8);
    visibility: hidden;
}


See this now? There's another red div within the first one, and it's stretched out all the way to the right. That's because this new div is, remember, 4 times the size of its parent.




We're going to populate movingBlockAspects with 4 divs. We'll use a For loop for that.
renderGrid: function()
{
    this.currentPosition = { x: 0, y: -4, r: 0};

    var grid = document.getElementById("grid");

    grid.innerHTML = "";

    var movingBlock = document.createElement("div");
    movingBlock.id = "movingBlock";

    var movingBlockAspects = document.createElement("div");
    movingBlockAspects.id = "movingBlockAspects";

    movingBlock.appendChild(movingBlockAspects);
    grid.appendChild(movingBlock);

    for (var i = 0; i <= 3; i++)
    {

    }


    this.positionBlock();

    var pauseScreen = document.createElement("div");
    pauseScreen.id = "pauseScreen";
    grid.appendChild(pauseScreen);

    var stopScreen = document.createElement("div");
    stopScreen.id = "stopScreen";
    grid.appendChild(stopScreen);
},


Inside the loop, create aspect as a new div. Its style is the aspect CSS class. The id will be a string constructed from "aspect" and the value of i. And after all that, append aspect to movingBlockAspects.
renderGrid: function()
{
    this.currentPosition = { x: 0, y: -4, r: 0};

    var grid = document.getElementById("grid");

    grid.innerHTML = "";

    var movingBlock = document.createElement("div");
    movingBlock.id = "movingBlock";

    var movingBlockAspects = document.createElement("div");
    movingBlockAspects.id = "movingBlockAspects";

    movingBlock.appendChild(movingBlockAspects);
    grid.appendChild(movingBlock);

    for (var i = 0; i <= 3; i++)
    {
        var aspect = document.createElement("div");
        aspect.className = "aspect";
        aspect.id = "aspect" + i;
        movingBlockAspects.appendChild(aspect);

    }

    this.positionBlock();

    var pauseScreen = document.createElement("div");
    pauseScreen.id = "pauseScreen";
    grid.appendChild(pauseScreen);

    var stopScreen = document.createElement("div");
    stopScreen.id = "stopScreen";
    grid.appendChild(stopScreen);
},


We are going to style aspect. There will be 4 such divs within movingBlockAspects, so the width will be a quarter of its parent. It will occupy full height and float left so that it should line up in a straight row.
#movingBlockAspects
{
    width: 400%;
    height: 100%;
    margin-left: 0%;
}

.aspect
{
    width: 25%;
    height: 100%;
    float: left;
}


#pauseScreen, #stopScreen
{
    position: absolute;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.8);
    visibility: hidden;
}


Here, we can see the long div seems to have been divided into 4 boxes! They are all divs styled using the aspect CSS class.




Now we complete this by declaring block and setting it to the element of the shapes array pointed to by the shape property of currentShape. Then we make sure that every div is populated with the block using the populateAspect() method. We pass in the id of the current element. block has 4 elements, one for each rotation, so we use i to get one rotation from block into each element. We then pass in the color of currentBlock and the name of the square CSS class.
renderGrid: function()
{
    this.currentPosition = { x: 0, y: -4, r: 0};

    var grid = document.getElementById("grid");

    grid.innerHTML = "";

    var block = this.shapes[this.currentBlock.shape];

    var movingBlock = document.createElement("div");
    movingBlock.id = "movingBlock";

    var movingBlockAspects = document.createElement("div");
    movingBlockAspects.id = "movingBlockAspects";

    movingBlock.appendChild(movingBlockAspects);
    grid.appendChild(movingBlock);

    for (var i = 0; i <= 3; i++)
    {
        var aspect = document.createElement("div");
        aspect.className = "aspect";
        aspect.id = "aspect" + i;
        movingBlockAspects.appendChild(aspect);
        this.populateAspect(aspect.id, block[i], this.currentBlock.color, "square");
    }

    this.positionBlock();

    var pauseScreen = document.createElement("div");
    pauseScreen.id = "pauseScreen";
    grid.appendChild(pauseScreen);

    var stopScreen = document.createElement("div");
    stopScreen.id = "stopScreen";
    grid.appendChild(stopScreen);
},


Now we define the square CSS class. It's like square_small, only twice the size.
.row
{
    width: 100%;
    height: 20px;
    float: left;
}

.square
{
    width: 20px;
    height: 20px;
    float: left;
}


.square_small
{
    width: 10px;
    height: 10px;
    float: left;
}


And here.
.white::before
{
    content: "";
    display: block;
    border: 1px solid rgb(255, 255, 255);
    background-color: rgba(255, 255, 255, 0.8);
}

.square::before
{
    width: 18px;
    height: 18px;
}    
        

.square_small::before
{
    width: 8px;
    height: 8px;
}    


And now populateAspects() has rendered the current block, in all its four rotations and the current color, inside movingBlockAspects!




One last thing before we conclude this part...

We need to fill grid up with squares. We have already defined the square CSS class. But we also want to pack them into separate rows. So, in the renderGrid() method, right after clearing grid of content, we use a For loop to iterate through the elements of grid.
renderGrid: function()
{
    this.currentPosition = { x: 0, y: -4, r: 0};

    var grid = document.getElementById("grid");

    grid.innerHTML = "";
    for(var i = 0; i < this.grid.length; i++)
    {

    }


    var block = this.shapes[this.currentBlock.shape];


In it, we define row as a new div element and style it using the row CSS class. And then append it to grid.
for(var i = 0; i < this.grid.length; i++)
{
    var row = document.createElement("div");
    row.className = "row";

    grid.appendChild(row);

}


This is the row CSS class. It takes up full width, but only 20 pixels in height, which is the same as the square CSS class. And we float it left so that the other divs styled using row fall to the next line neatly.
#grid
{
    width: 200px;
    height: 400px;
    background-color: rgba(0, 0, 0, 0.5);
    margin: 5px auto 5px auto;
    position: relative;
    border: 5px inset rgb(155, 155, 155);
}

.row
{
    width: 100%;
    height: 20px;
    float: left;
}


.square
{
    width: 20px;
    height: 20px;
    float: left;
}


Now you see the rows within grid.




Right before appending row to grid, use a nested For loop to iterate through the elements of grid's current element.
for(var i = 0; i < this.grid.length; i++)
{
    var row = document.createElement("div");
    row.className = "row";

    for(var j = 0; j < this.grid[i].length; j++)
    {

    }


    grid.appendChild(row);
}


In here, create square as a new div. It's styled using the square CSS class and whatever color that this current sub-element in grid has. And finally, we append square to row.
for(var i = 0; i < this.grid.length; i++)
{
    var row = document.createElement("div");
    row.className = "row";

    for(var j = 0; j < this.grid[i].length; j++)
    {
        var square = document.createElement("div");
        square.className = "square " + this.grid[i][j];
        row.appendChild(square);

    }

    grid.appendChild(row);
}


Now we can see all the squares within the individual rows.




The red outlines so far have given us a good idea where the divs are located. There's no more HTML to be created, either "manually" or via JavaScript.

Next

Directional controls!

No comments:

Post a Comment