
Playaround with LWC using html canvas.
-- simpleTetris.html
<template>
<article class="slds-card">
<canvas id="tPanel" width="240" height="400">
</canvas>
</article>
</template>
-- simpleTetris.js
import { LightningElement } from 'lwc';
let pCanvas , context; //canvas and it context 2d
let playBoard = {};
let lastTime = 0;
let dropInterval = 1000;
let dropCounter = 0;
const player = {
position : {x:0,y:0},
matrix:null,
score: 0
};
const colors = [
null,
'#FF0D72',
'#0DC2FF',
'#0DFF72',
'#F538FF',
'#FF8E0D',
'#FFE138',
'#3877FF',
];
let IBlock = [
[0,1,0,0],
[0,1,0,0],
[0,1,0,0],
[0,1,0,0],
];
let LBlock = [
[0,2,0],
[0,2,0],
[0,2,2],
];
let JBlock = [
[0,3,0],
[0,3,0],
[3,3,0],
];
let OBlock = [
[4,4],
[4,4],
];
let ZBlock = [
[5,5,0],
[0,5,5],
[0,0,0],
];
let SBlock = [
[0,6,6],
[6,6,0],
[0,0,0],
];
let TBlock = [
[0,7,0],
[7,7,7],
[0,0,0],
];
export default class SimpleTetris extends LightningElement {
constructor(){
super();
document.addEventListener('keydown',this.handleKeyDown.bind(this));
}
renderedCallback(){
pCanvas = this.template.querySelector('canvas');
context = pCanvas.getContext('2d');
context.scale(20,20);
playBoard = this.createMatrix(12,20);
this.resetPlayer();
this.update();
}
movePlayer(pos){
player.position.x += pos;
if(this.collide(playBoard,player)){
player.position.x -= pos;
}
}
rotatePlayer(d){
const pos = player.position.x;
let offset = 1;
this.rotateMatrix(player.matrix,d);
while(this.collide(playBoard,player)){
player.position.x += offset;
offset = -(offset + (offset > 0 ? 1 : -1));
if(offset > player.matrix[0].length){
this.rotateMatrix(player.matrix, -d);
player.position.x = pos;
return;
}
}
}
handleKeyDown(event){
console.log(event.keyCode);
if(event.keyCode === 37){ // left
this.movePlayer(-1);
} else if(event.keyCode === 39){// right
this.movePlayer(1);
} else if(event.keyCode === 40){// down
this.dropPlayer();
} else if(event.keyCode == 38){
this.rotatePlayer(1);
}
}
draw(){
context.fillStyle = '#000';
context.fillRect(0,0,pCanvas.width,pCanvas.height);
this.drawMatrix(playBoard,{x:0,y:0});
this.drawMatrix(player.matrix,player.position);
//console.log(player.position.x, player.position.y);
}
mergeBlock(pb,p){
p.matrix.forEach((row,y) =>{
row.forEach((value,x) => {
if(value !==0){
pb[y+p.position.y][x+p.position.x] = value;
}
});
});
}
createMatrix(r,c){
const matrix = [];
while(c--){
matrix.push(new Array(r).fill(0));
}
return matrix;
}
clearRow(){
let rowC = 1;
outer : for(let y = playBoard.length - 1; y > 0;--y){
for (let x = 0;x < playBoard[y].length; ++x){
if(playBoard[y][x] == 0){
continue outer;
}
}
const row = playBoard.splice(y,1)[0].fill(0);
playBoard.unshift(row);
++y;
rowC *=2;
}
}
drawMatrix(m,offset){
m.forEach((row,y) => {
row.forEach((value,x)=>{
if(value !== 0){
context.fillStyle = colors[value];
context.fillRect(x+offset.x,
y+offset.y,
1,1);
}
});
});
}
rotateMatrix(m,dir){
for (let y = 0; y< m.length;++y){
for(let x = 0; x < y; ++x){
[m[x][y],m[y][x]]=[m[y][x],m[x][y]];
}
}
if(dir > 0){
m.forEach(row => row.reverse());
}else{
m.reverse;
}
}
createBlocks(block){
if(block === 'I'){
return IBlock;
}else if(block === 'L'){
return LBlock;
}else if (block === 'J'){
return JBlock;
}else if (block === 'O'){
return OBlock;
}else if (block === 'Z'){
return ZBlock;
}else if(block === 'S'){
return SBlock;
}else if(block === 'T'){
return TBlock;
}
}
resetPlayer(){
const blocks = 'TJLOSZI';
player.matrix = this.createBlocks(blocks[blocks.length * Math.random() | 0]);
player.position.y = 0;
player.position.x = (playBoard[0].length / 2 | 0) - (player.matrix[0].length/2 | 0);
if(this.collide(playBoard,player)){
playBoard.forEach(row => row.full(0));
player.score = 0;
}
}
dropPlayer(){
player.position.y++;
if(this.collide(playBoard,player)){
player.position.y--;
this.mergeBlock(playBoard,player);
this.resetPlayer();
this.clearRow();
}
dropCounter=0;
}
collide(playB,p){
const m = p.matrix;
const o = p.position;
for (let y = 0 ; y<m.length;++y){
for (let x = 0 ; x < m[y].length; ++x){
if(m[y][x] !== 0 &&
(playB[y+o.y] && playB[y+o.y][x+o.x]) !== 0){
return true;
}
}
}
return false;
}
update(time=0){
const deltaTime = time-lastTime;
dropCounter += deltaTime;
if(dropCounter > dropInterval){
this.dropPlayer();
}
lastTime = time;
this.draw();
requestAnimationFrame((ts)=>{this.update(ts)});
}
}