Как увеличить скорость падения фигуры в Тетрис?

112
23 ноября 2021, 01:50

Нужно реализовать увеличение скорости падения фигуры с каждым убранным уровнем. Кажется уже всё перепробовал, уже глаза замылились. Кто поможет, тому буду безмерно благодарен.

"use strict"; 
 
(function() { 
  const canvas = document.getElementById("tetris"); 
  const context = canvas.getContext("2d"); 
  context.scale(20, 20); 
  let makeMatrix = function(w, h) { 
    const matrix = []; 
    while (h--) { 
      matrix.push(new Array(w).fill(0)); 
    } 
    return matrix; 
  }; 
 
  let makePiece = function(type) { 
    if (type === "i") { 
      return [ 
        [0, 1, 0, 0], 
        [0, 1, 0, 0], 
        [0, 1, 0, 0], 
        [0, 1, 0, 0] 
      ]; 
    } else if (type === "j") { 
      return [ 
        [0, 2, 0], 
        [0, 2, 0], 
        [2, 2, 0] 
      ]; 
    } else if (type === "l") { 
      return [ 
        [0, 3, 0], 
        [0, 3, 0], 
        [0, 3, 3] 
      ]; 
    } else if (type === "o") { 
      return [ 
        [4, 4], 
        [4, 4] 
      ]; 
    } else if (type === "s") { 
      return [ 
        [0, 5, 5], 
        [5, 5, 0], 
        [0, 0, 0] 
      ]; 
    } else if (type === "t") { 
      return [ 
        [0, 6, 0], 
        [6, 6, 6], 
        [0, 0, 0] 
      ]; 
    } else if (type === "z") { 
      return [ 
        [7, 7, 0], 
        [0, 7, 7], 
        [0, 0, 0] 
      ]; 
    } 
  }; 
 
  let points = function() { 
    let rowCount = 1; 
    outer: for (let y = area.length - 1; y > 0; --y) { 
      for (let x = 0; x < area[y].length; ++x) { 
        if (area[y][x] === 0) { 
          continue outer; 
        } 
      } 
      const row = area.splice(y, 1)[0].fill(0); 
      area.unshift(row); 
      ++y; 
      player.score += rowCount; 
    } 
  } 
  let collide = function(area, player) { 
    const [m, o] = [player.matrix, player.pos]; 
    for (let y = 0; y < m.length; ++y) { 
      for (let x = 0; x < m[y].length; ++x) { 
        if (m[y][x] !== 0 && (area[y + o.y] && area[y + o.y][x + o.x]) !== 0) { 
          return true; 
        } 
      } 
    } 
    return false; 
  }; 
  let drawMatrix = function(matrix, offset) { 
    matrix.forEach((row, y) => { 
      row.forEach((value, x) => { 
        if (value !== 0) { 
          let imgTag = document.createElement("IMG"); 
          imgTag.src = colors[value]; 
          context.drawImage(imgTag, x + offset.x, y + offset.y, 1, 1); 
        } 
      }); 
    }); 
  }; 
  let merge = function(area, player) { 
    player.matrix.forEach((row, y) => { 
      row.forEach((value, x) => { 
        if (value !== 0) { 
          area[y + player.pos.y][x + player.pos.x] = value; 
        } 
      }); 
    }); 
  }; 
  let rotate = function(matrix, dir) { 
    for (let y = 0; y < matrix.length; ++y) { 
      for (let x = 0; x < y; ++x) { 
        [ 
          matrix[x][y], 
          matrix[y][x] 
        ] = [ 
          matrix[y][x], 
          matrix[x][y], 
        ] 
      } 
    } 
    if (dir > 0) { 
      matrix.forEach(row => row.reverse()); 
    } else { 
      matrix.reverse(); 
    } 
  }; 
  let playerReset = function() { 
    const pieces = "ijlostz"; 
    player.matrix = makePiece(pieces[Math.floor(Math.random() * pieces.length)]); 
    player.pos.y = 0; 
    player.pos.x = (Math.floor(area[0].length / 2)) - (Math.floor(player.matrix[0].length / 2)); 
    if (collide(area, player)) { 
      area.forEach(row => row.fill(0)); 
      player.score = 0; 
      gameRun = false; 
    } 
  }; 
  let playerDrop = function() { 
    player.pos.y++; 
    if (collide(area, player)) { 
      player.pos.y--; 
      merge(area, player); 
      points(); 
      playerReset(); 
    } 
  }; 
  let playerMove = function(dir) { 
    player.pos.x += dir; 
    if (collide(area, player)) { 
      player.pos.x -= dir; 
    } 
  }; 
  let playerRotate = function(dir) { 
    const pos = player.pos.x; 
    let offset = 1; 
    rotate(player.matrix, dir); 
    while (collide(area, player)) { 
      player.pos.x += offset; 
      offset = -(offset + (offset > 0 ? 1 : -1)); 
      if (offset > player.matrix[0].length) { 
        rotate(player.matrix, -dir); 
        player.pos.x = pos; 
        return; 
      } 
    } 
  }; 
  let draw = function() { 
    context.clearRect(0, 0, canvas.width, canvas.height); 
    context.fillStyle = "#000000"; 
    context.fillRect(0, 0, canvas.width, canvas.height); 
    updateScore(); 
    drawMatrix(area, { 
      x: 0, 
      y: 0 
    }); 
    drawMatrix(player.matrix, player.pos); 
  }; 
  let dropInter = 100; 
  let time = 0; 
  let update = function() { 
    time++; 
    if (time >= dropInter) { 
      playerDrop(); 
      time = 0; 
    } 
    draw(); 
  }; 
  let updateScore = function() { 
    context.font = "bold 1px Arial"; 
    context.fillStyle = "#ffffff"; 
    context.textAlign = "left"; 
    context.textBaseline = "top"; 
    context.fillText("Lines:" + player.score, 0.1, 0.1); 
  }; 
  // let addSpeed = function() { 
  // 	if (updateScore > 0) { 
  // 		dropInter -= 5; 
  // 	} 
  // }; 
  // addSpeed(); 
  let gameOver = function() { 
    clearInterval(gameLoop); 
    context.font = "2px Arial"; 
    context.fillStyle = "#ffffff"; 
    context.textAlign = "center"; 
    context.textBaseline = "middle"; 
    context.fillText("GAME OVER", (canvas.width / 20) / 2, (canvas.width / 20) / 2); 
    document.getElementById("start_game").disabled = false; 
  }; 
  const colors = [ 
    null, 
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAABASURBVFhH7dcxEQAgDATBgBpkRiZugBnQQJq96tNtm7ZOUdgF5Hjn53JGf7MsAAAAAAAAAAAAAAAAAIDi9zxiA/4+DjI3K6uXAAAAAElFTkSuQmCC", 
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAABFSURBVFhH7dehEQAgDATBh5ZoAEvF2DRATcAwqYE3d+rj1qbsm4w9QB8rz7/FbKq5bQEAAAAAAAAAAAAAAAAAAADm91w6cr4PMmFrragAAAAASUVORK5CYII=", 
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAABGSURBVFhH7dcxEQAgDATBB3FowAs1XqIBc8Aw0cA3d9Wn2zZl32TsAdboef6tzVDNbQsAAAAAAAAAAAAAAAAAAAAwv+fSAYyGDzIDc/xyAAAAAElFTkSuQmCC", 
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAGLH901AAAACXBIWXMAAAsSAAALEgHS3X78AAAAa0lEQVR4nGP5//8/AwywAPGy01YIDooMgoOiB66BoB5kldhVUSiB7C4UCaw2U9lyKkrgCiuSTRrVQJEGXOkJpwaSYi3K9Nhg9PSohlENODSQWi6RBMjJDqSCUQtGLRi1YNSCEWMBsFClnQUAHyUliPhfuZ4AAAAASUVORK5CYII=", 
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAABESURBVFhH7dcxEQAgDATBB3lYAHnEAvaAYaKBb+6qT7dtyr7J2APMFnn+bayumtsWAAAAAAAAAAAAAAAAAAAAYH7PpQOPVg8yEVQxkwAAAABJRU5ErkJggg==", 
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAABFSURBVFhH7dehEQAgDATBhxLwSPovCBlPC8AwqYE3d+rj1qbsm4w9wOojz7+1mKq5bQEAAAAAAAAAAAAAAAAAAADm91w6YO4PMgLP8KUAAAAASUVORK5CYII=", 
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAABCSURBVFhH7dcxEQAgDATBgAHc4L/GDQqAGdBAmr3q022bsk6R2AWM9s7P9Rn1zbQAAAAAAAAAAAAAAAAAAJLf84gNFUUOMkwQM24AAAAASUVORK5CYII=" 
  ]; 
  const area = makeMatrix(14, 22); 
  const player = { 
    pos: { 
      x: 0, 
      y: 0 
    }, 
    matrix: null, 
    score: 0 
  }; 
  const move = 1; 
  let gameLoop; 
  let gameRun = false; 
  playerReset(); 
  draw(); 
  gameOver(); 
  document.addEventListener('keydown', function(e) { 
    if (e.keyCode === 37) { 
      playerMove(-move); 
    } else if (e.keyCode === 39) { 
      playerMove(+move); 
    } else if (e.keyCode === 38) { 
      playerRotate(-move); 
    } else if (e.keyCode === 40) { 
      playerDrop(); 
    } 
  }); 
  document.getElementById("start_game").onclick = function() { 
    gameRun = true; 
    playerReset(); 
    let t = 10; 
    gameLoop = setInterval(function() { 
      if (gameRun) { 
        update(); 
      } else { 
        gameOver(); 
      } 
    }, t); 
    this.disabled = false; 
  }; 
})();
body { 
  background-color: #737373; 
} 
 
* { 
  margin: 0; 
  padding: 0; 
} 
 
.canvas_wrap { 
  display: block; 
  margin: 0 auto; 
  padding-top: 25px; 
} 
 
.canvas_wrap>* { 
  display: block; 
  margin: 0 auto; 
  border: 1px solid #ffffff; 
} 
 
.canvas_wrap>button { 
  font-family: Arial, Helvetica, sans-serif; 
  font-size: 30px; 
  font-weight: bold; 
  border: none; 
  color: #ffffff; 
  background-color: #000000; 
  padding: 5px; 
  margin: 10px auto; 
  border: 1px solid #000000; 
  outline: none; 
} 
 
.canvas_wrap>button:hover { 
  cursor: pointer; 
  background-color: #ffffff; 
  color: #000000; 
}
<!DOCTYPE html> 
<html lang="en"> 
 
<head> 
  <meta charset="UTF-8"> 
  <meta name="viewport" content="width=device-width, initial-scale=1.0"> 
  <meta http-equiv="X-UA-Compatible" content="ie=edge"> 
  <title>Tetris</title> 
  <link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAGLH901AAAACXBIWXMAAAsSAAALEgHS3X78AAAAa0lEQVR4nGP5//8/AwywAPGy01YIDooMgoOiB66BoB5kldhVUSiB7C4UCaw2U9lyKkrgCiuSTRrVQJEGXOkJpwaSYi3K9Nhg9PSohlENODSQWi6RBMjJDqSCUQtGLRi1YNSCEWMBsFClnQUAHyUliPhfuZ4AAAAASUVORK5CYII=" 
    type="image/x-icon"> 
  <link rel="stylesheet" href="style.css"> 
</head> 
 
<body> 
  <div class="canvas_wrap"> 
    <canvas id="tetris" width="280" height="440"></canvas> 
    <button type="button" id="start_game">Start</button> 
  </div> 
</body> 
<script src="script.js"></script> 
 
</html>

Answer 1
dropInter = Math.max(dropInter - 5, 20);

Имеет смысл рассмотреть вариант с переменным декрементом - уменьшающимся вместе с значением dropInter.

"use strict"; 
 
(function() { 
  const canvas = document.getElementById("tetris"); 
  const context = canvas.getContext("2d"); 
  context.scale(20, 20); 
  let makeMatrix = function(w, h) { 
    const matrix = []; 
    while (h--) { 
      matrix.push(new Array(w).fill(0)); 
    } 
    return matrix; 
  }; 
 
  let makePiece = function(type) { 
    if (type === "i") { 
      return [ 
        [0, 1, 0, 0], 
        [0, 1, 0, 0], 
        [0, 1, 0, 0], 
        [0, 1, 0, 0] 
      ]; 
    } else if (type === "j") { 
      return [ 
        [0, 2, 0], 
        [0, 2, 0], 
        [2, 2, 0] 
      ]; 
    } else if (type === "l") { 
      return [ 
        [0, 3, 0], 
        [0, 3, 0], 
        [0, 3, 3] 
      ]; 
    } else if (type === "o") { 
      return [ 
        [4, 4], 
        [4, 4] 
      ]; 
    } else if (type === "s") { 
      return [ 
        [0, 5, 5], 
        [5, 5, 0], 
        [0, 0, 0] 
      ]; 
    } else if (type === "t") { 
      return [ 
        [0, 6, 0], 
        [6, 6, 6], 
        [0, 0, 0] 
      ]; 
    } else if (type === "z") { 
      return [ 
        [7, 7, 0], 
        [0, 7, 7], 
        [0, 0, 0] 
      ]; 
    } 
  }; 
 
  let points = function() { 
    let rowCount = 1; 
    outer: for (let y = area.length - 1; y > 0; --y) { 
      for (let x = 0; x < area[y].length; ++x) { 
        if (area[y][x] === 0) { 
          continue outer; 
        } 
      } 
      const row = area.splice(y, 1)[0].fill(0); 
      area.unshift(row); 
/* *** */ 
dropInter = Math.max(dropInter - 10, 5); 
console.log("dropInter =", dropInter); 
      ++y; 
      player.score += rowCount; 
    } 
  } 
  let collide = function(area, player) { 
    const [m, o] = [player.matrix, player.pos]; 
    for (let y = 0; y < m.length; ++y) { 
      for (let x = 0; x < m[y].length; ++x) { 
        if (m[y][x] !== 0 && (area[y + o.y] && area[y + o.y][x + o.x]) !== 0) { 
          return true; 
        } 
      } 
    } 
    return false; 
  }; 
  let drawMatrix = function(matrix, offset) { 
    matrix.forEach((row, y) => { 
      row.forEach((value, x) => { 
        if (value !== 0) { 
          let imgTag = document.createElement("IMG"); 
          imgTag.src = colors[value]; 
          context.drawImage(imgTag, x + offset.x, y + offset.y, 1, 1); 
        } 
      }); 
    }); 
  }; 
  let merge = function(area, player) { 
    player.matrix.forEach((row, y) => { 
      row.forEach((value, x) => { 
        if (value !== 0) { 
          area[y + player.pos.y][x + player.pos.x] = value; 
        } 
      }); 
    }); 
  }; 
  let rotate = function(matrix, dir) { 
    for (let y = 0; y < matrix.length; ++y) { 
      for (let x = 0; x < y; ++x) { 
        [ 
          matrix[x][y], 
          matrix[y][x] 
        ] = [ 
          matrix[y][x], 
          matrix[x][y], 
        ] 
      } 
    } 
    if (dir > 0) { 
      matrix.forEach(row => row.reverse()); 
    } else { 
      matrix.reverse(); 
    } 
  }; 
  let playerReset = function() { 
    const pieces = "ijlostz"; 
    player.matrix = makePiece(pieces[Math.floor(Math.random() * pieces.length)]); 
    player.pos.y = 0; 
    player.pos.x = (Math.floor(area[0].length / 2)) - (Math.floor(player.matrix[0].length / 2)); 
    if (collide(area, player)) { 
      area.forEach(row => row.fill(0)); 
      player.score = 0; 
      gameRun = false; 
    } 
  }; 
  let playerDrop = function() { 
    player.pos.y++; 
    if (collide(area, player)) { 
      player.pos.y--; 
      merge(area, player); 
      points(); 
      playerReset(); 
    } 
  }; 
  let playerMove = function(dir) { 
    player.pos.x += dir; 
    if (collide(area, player)) { 
      player.pos.x -= dir; 
    } 
  }; 
  let playerRotate = function(dir) { 
    const pos = player.pos.x; 
    let offset = 1; 
    rotate(player.matrix, dir); 
    while (collide(area, player)) { 
      player.pos.x += offset; 
      offset = -(offset + (offset > 0 ? 1 : -1)); 
      if (offset > player.matrix[0].length) { 
        rotate(player.matrix, -dir); 
        player.pos.x = pos; 
        return; 
      } 
    } 
  }; 
  let draw = function() { 
    context.clearRect(0, 0, canvas.width, canvas.height); 
    context.fillStyle = "#000000"; 
    context.fillRect(0, 0, canvas.width, canvas.height); 
    updateScore(); 
    drawMatrix(area, { 
      x: 0, 
      y: 0 
    }); 
    drawMatrix(player.matrix, player.pos); 
  }; 
  let dropInter = 100; 
  let time = 0; 
  let update = function() { 
    time++; 
    if (time >= dropInter) { 
      playerDrop(); 
      time = 0; 
    } 
    draw(); 
  }; 
  let updateScore = function() { 
    context.font = "bold 1px Arial"; 
    context.fillStyle = "#ffffff"; 
    context.textAlign = "left"; 
    context.textBaseline = "top"; 
    context.fillText("Lines:" + player.score, 0.1, 0.1); 
  }; 
  // let addSpeed = function() { 
  // 	if (updateScore > 0) { 
  // 		dropInter -= 5; 
  // 	} 
  // }; 
  // addSpeed(); 
  let gameOver = function() { 
    clearInterval(gameLoop); 
    context.font = "2px Arial"; 
    context.fillStyle = "#ffffff"; 
    context.textAlign = "center"; 
    context.textBaseline = "middle"; 
    context.fillText("GAME OVER", (canvas.width / 20) / 2, (canvas.width / 20) / 2); 
    document.getElementById("start_game").disabled = false; 
  }; 
  const colors = [ 
    null, 
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAABASURBVFhH7dcxEQAgDATBgBpkRiZugBnQQJq96tNtm7ZOUdgF5Hjn53JGf7MsAAAAAAAAAAAAAAAAAIDi9zxiA/4+DjI3K6uXAAAAAElFTkSuQmCC", 
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAABFSURBVFhH7dehEQAgDATBh5ZoAEvF2DRATcAwqYE3d+rj1qbsm4w9QB8rz7/FbKq5bQEAAAAAAAAAAAAAAAAAAADm91w6cr4PMmFrragAAAAASUVORK5CYII=", 
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAABGSURBVFhH7dcxEQAgDATBB3FowAs1XqIBc8Aw0cA3d9Wn2zZl32TsAdboef6tzVDNbQsAAAAAAAAAAAAAAAAAAAAwv+fSAYyGDzIDc/xyAAAAAElFTkSuQmCC", 
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAGLH901AAAACXBIWXMAAAsSAAALEgHS3X78AAAAa0lEQVR4nGP5//8/AwywAPGy01YIDooMgoOiB66BoB5kldhVUSiB7C4UCaw2U9lyKkrgCiuSTRrVQJEGXOkJpwaSYi3K9Nhg9PSohlENODSQWi6RBMjJDqSCUQtGLRi1YNSCEWMBsFClnQUAHyUliPhfuZ4AAAAASUVORK5CYII=", 
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAABESURBVFhH7dcxEQAgDATBB3lYAHnEAvaAYaKBb+6qT7dtyr7J2APMFnn+bayumtsWAAAAAAAAAAAAAAAAAAAAYH7PpQOPVg8yEVQxkwAAAABJRU5ErkJggg==", 
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAABFSURBVFhH7dehEQAgDATBhxLwSPovCBlPC8AwqYE3d+rj1qbsm4w9wOojz7+1mKq5bQEAAAAAAAAAAAAAAAAAAADm91w6YO4PMgLP8KUAAAAASUVORK5CYII=", 
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAABCSURBVFhH7dcxEQAgDATBgAHc4L/GDQqAGdBAmr3q022bsk6R2AWM9s7P9Rn1zbQAAAAAAAAAAAAAAAAAAJLf84gNFUUOMkwQM24AAAAASUVORK5CYII=" 
  ]; 
  const area = makeMatrix(14, 22); 
  const player = { 
    pos: { 
      x: 0, 
      y: 0 
    }, 
    matrix: null, 
    score: 0 
  }; 
  const move = 1; 
  let gameLoop; 
  let gameRun = false; 
  playerReset(); 
  draw(); 
  gameOver(); 
  document.addEventListener('keydown', function(e) { 
    if (e.keyCode === 37) { 
      playerMove(-move); 
    } else if (e.keyCode === 39) { 
      playerMove(+move); 
    } else if (e.keyCode === 38) { 
      playerRotate(-move); 
    } else if (e.keyCode === 40) { 
      playerDrop(); 
    } 
  }); 
  document.getElementById("start_game").onclick = function() { 
    gameRun = true; 
    playerReset(); 
    let t = 10; 
    gameLoop = setInterval(function() { 
      if (gameRun) { 
        update(); 
      } else { 
        gameOver(); 
      } 
    }, t); 
    this.disabled = false; 
  }; 
})();
body { 
  background-color: #737373; 
} 
 
* { 
  margin: 0; 
  padding: 0; 
} 
 
.canvas_wrap { 
  display: block; 
  margin: 0 auto; 
  padding-top: 25px; 
} 
 
.canvas_wrap>* { 
  display: block; 
  margin: 0 auto; 
  border: 1px solid #ffffff; 
} 
 
.canvas_wrap>button { 
  font-family: Arial, Helvetica, sans-serif; 
  font-size: 30px; 
  font-weight: bold; 
  border: none; 
  color: #ffffff; 
  background-color: #000000; 
  padding: 5px; 
  margin: 10px auto; 
  border: 1px solid #000000; 
  outline: none; 
} 
 
.canvas_wrap>button:hover { 
  cursor: pointer; 
  background-color: #ffffff; 
  color: #000000; 
}
<!DOCTYPE html> 
<html lang="en"> 
 
<head> 
  <meta charset="UTF-8"> 
  <meta name="viewport" content="width=device-width, initial-scale=1.0"> 
  <meta http-equiv="X-UA-Compatible" content="ie=edge"> 
  <title>Tetris</title> 
  <link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAGLH901AAAACXBIWXMAAAsSAAALEgHS3X78AAAAa0lEQVR4nGP5//8/AwywAPGy01YIDooMgoOiB66BoB5kldhVUSiB7C4UCaw2U9lyKkrgCiuSTRrVQJEGXOkJpwaSYi3K9Nhg9PSohlENODSQWi6RBMjJDqSCUQtGLRi1YNSCEWMBsFClnQUAHyUliPhfuZ4AAAAASUVORK5CYII=" 
    type="image/x-icon"> 
  <link rel="stylesheet" href="style.css"> 
</head> 
 
<body> 
  <div class="canvas_wrap"> 
    <canvas id="tetris" width="280" height="440"></canvas> 
    <button type="button" id="start_game">Start</button> 
  </div> 
</body> 
<script src="script.js"></script> 
 
</html>

READ ALSO
Поясните код из JavaScript (про localStorage)

Поясните код из JavaScript (про localStorage)

В общем нужно записать отмеченные чекбоксыы (для отображения после перезагрузки)В интернете нашел решение, но не могу понять код (в основном...

66
Асинхронная очередь запросов на сервер с фиксированным интервалом на JavaScript

Асинхронная очередь запросов на сервер с фиксированным интервалом на JavaScript

Есть код синхронного запросаСинхронный используется потому, что нельзя отправлять параллельные запросы, сервер отвечает не чаще раза в секунду,...

76
Ошибка при создании слайдера через объект

Ошибка при создании слайдера через объект

При запуске кода изображения не изменяются - указывается ошибка

162
проблема в Django форме

проблема в Django форме

Учу Django, и осваиваю встроиные Django формыпроблема в том что у меня есть форма, которая должна либо создавать тег либо выводить ошибку(думаю...

209