俄罗斯方块

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <title>俄罗斯方块</title>
  <style type="text/css">
    * {
      box-sizing: border-box;
    }

    #toolbar {
      width: 222px;
      margin: 0 auto 5px;
    }

    #toolbar > button {
      font-size: 12px;
      padding: 2px 5px;
      border-radius: 5px;
      cursor: pointer;
      outline: none;
      width: 100px;
    }

    #canvas-container {
      box-sizing: content-box;
      width: 220px;
      margin: 0 auto;
      border: 1px solid #555;
      background: #f0f0f0;
    }

    #canvas-container > canvas {
      vertical-align: top;
    }
  </style>
</head>
<body>
  <div id='toolbar'>
    <Button onclick='onClickStart(event)'>开始</Button>
    <Button onclick='onClickPause(event)'>暂停</Button>
  </div>
  <div id='canvas-container'>
    <canvas width="220px" height="440px">你的浏览器不支持canvas,请升级你的浏览器</canvas>
  </div>
  <script type="text/javascript">
    CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
      if (w < 2 * r) {r = w / 2;}
      if (h < 2 * r){ r = h / 2;}
      this.beginPath();
      this.moveTo(x+r, y);
      this.arcTo(x+w, y, x+w, y+h, r);
      this.arcTo(x+w, y+h, x, y+h, r);
      this.arcTo(x, y+h, x, y, r);
      this.arcTo(x, y, x+w, y, r);
      this.closePath();
      return this;
    }

    function createEmptyCubes() {
      return (new Array(20)).fill(0).map(() => (new Array(10)).fill(0));
    }

    var canvas = document.getElementsByTagName('canvas')[0];
    var context = canvas.getContext('2d');
    var cubes = createEmptyCubes();
    var shapes = {
      S1: [[1, 2], [2, 2], [2, 1], [3, 1]], 
      S2: [[1, 0], [1, 1], [2, 1], [2, 2]],
      Z1: [[1, 1], [2, 1], [2, 2], [3, 2]],
      Z2: [[2, 0], [2, 1], [1, 1], [1, 2]],
      L1: [[1, 0], [1, 1], [1, 2], [2, 2]],
      L2: [[1, 2], [2, 2], [3, 2], [3, 1]],
      L3: [[1, 0], [2, 0], [2, 1], [2, 2]],
      L4: [[1, 1], [1, 2], [2, 1], [3, 1]],
      J1: [[2, 0], [2, 1], [2, 2], [1, 2]],
      J2: [[1, 1], [1, 2], [2, 2], [3, 2]],
      J3: [[1, 0], [1, 1], [1, 2], [2, 0]],
      J4: [[1, 1], [2, 1], [3, 1], [3, 2]],
      I1: [[1, 0], [1, 1], [1, 2], [1, 3]],
      I2: [[0, 1], [1, 1], [2, 1], [3, 1]],
      O: [[1, 1], [2, 1], [1, 2], [2, 2]],
      T1: [[1, 1], [2, 1], [3, 1], [2, 2]],
      T2: [[2, 0], [2, 1], [2, 2], [3, 1]],
      T3: [[2, 0], [1, 1], [2, 1], [3, 1]],
      T4: [[1, 1], [2, 0], [2, 1], [2, 2]]
    }
    var colors = {
      S: '#19f219',
      Z: '#d30200',
      L: '#d76400',
      J: '#0600d1',
      I: '#19f2f2',
      O: '#d6d501',
      T: '#aaf219'
    };
    var MAX_LEVEL = 10;
    var SCORE_PER_LEVEL = 500;
    var shapeX = 3;
    var shapeY = 0;
    var shape = 'J1';
    var fall = false;
    var duration = 500;
    var timerId = null;
    var over = false;
    var score = 0;
    var level = 0;

    function getLevelFromScore(score) {
      return Math.floor(score / SCORE_PER_LEVEL) + 1;
    }

    function getDurationFromLevel(level) {
      if (level > 10) {
        return 50;
      } else {
        return 550 - level * 50;
      }
    }

    function drawScore(score, level) {
      context.save();
      context.fillStyle = 'black';
      context.font = '14px 隶书';
      context.fillText(`分数:${score}`, 5, 15);
      context.textAlign = 'right';
      context.fillText(`Level ${level}`, 215, 15);
      context.restore();
    }

    function drawCube(x, y) {
      context.roundRect(1 + x * 22, 1 + y * 22, 20, 20, 4);
      context.fill();
    }

    function drawShape(shape, x, y) {
      context.save();
      context.fillStyle = colors[shape[0]];
      for (const arr of shapes[shape]) {
        drawCube(x + arr[0], y + arr[1]);
      }
      context.restore();
    }

    function drawBoard() {
      for (let y = 0; y < 20; y++) {
        for (let x = 0; x < 10; x++) {
          if (cubes[y][x]) {
            context.save();
            context.fillStyle = cubes[y][x];
            drawCube(x, y);
            context.restore();
          }
        }
      }
    }

    function drawGameOver() {
      context.save();
      context.fillStyle = 'rgba(100, 100, 100, 0.6)';
      context.fillRect(0, 0, 220, 440);
      context.fillStyle = 'white';
      context.font = "30px Georgia";
      context.fillText("Game Over", 30, 220);
      context.restore();
    }

    function drawSuccess() {
      context.save();
      context.fillStyle = 'rgba(100, 100, 100, 0.6)';
      context.fillRect(0, 0, 220, 440);
      context.fillStyle = 'red';
      context.font = "40px Georgia";
      context.fillText("恭喜通关", 30, 220);
      context.restore();
    }

    function drawPause() {
      context.save();
      context.fillStyle = 'rgba(100, 100, 100, 0.6)';
      context.fillRect(0, 0, 220, 440);
      context.fillStyle = 'white';
      context.font = "30px Georgia";
      context.fillText("Game Pause", 30, 220);
      context.restore();
    }

    function drawStart() {
      for (let y = 12; y < 20; y++) {
        for (let x = 0; x < 10; x++) {
          drawCube(x, y);
        }
      }

      drawScore(0, 0);

      context.save();
      context.fillStyle = 'rgba(100, 100, 100, 0.6)';
      context.fillRect(0, 0, 220, 440);
      context.fillStyle = 'white';
      context.font = "30px Georgia";
      context.fillText("Start Game", 30, 220);
      context.restore();
    }

    function update() {
      context.clearRect(0, 0, 220, 440);
      drawBoard();

      if (shape) {
        drawShape(shape, shapeX, shapeY);
      }

      drawScore(score, level);

      if (over) {
        drawGameOver();
      }
    }

    function randomShape() {
      const shape = ['S1', 'S2', 'Z1', 'Z2', 'L1', 'L2', 'L3', 'L4', 'J1', 'J2', 'J3', 'J4', 'I1', 'I2', 'O', 'T1', 'T2', 'T3', 'T4'];
      const index = Math.floor(Math.random() * shape.length);
      return shape[index];
    }

    function isOverflow(x, y) {
      return x < 0 || x > 9 || y > 19;
    }

    function isOverlap(x, y) {
      return cubes[y][x];
    }

    function overflow(x, y) {
      return shapes[shape].some(cord => isOverflow(cord[0] + x, cord[1] + y) || isOverlap(cord[0] + x, cord[1] + y));
    }

    function copyCurrentShapeToCubes() {
      for (const cord of shapes[shape]) {
        cubes[cord[1] + shapeY][cord[0] + shapeX] = colors[shape[0]];
      }
    }

    function clearRow(row) {
      for (let x = 0; x < 10; x++) {
        cubes[row][x] = 0;
      }
    }

    function moveDown(row) {
      for (let y = row; y > 0; y--) {
        for (let x = 0; x < 10; x++) {
          cubes[y][x] = cubes[y - 1][x];
        }
      }
      clearRow(0);
    }

    function clearRows() {
      var row = 0;
      for (let y = 19; y > -1; y--) {
        if (cubes[y].every(value => value)) {
          moveDown(y);
          y++;
          row++;
        }
      }
      return row;
    }

    function rowToScore(row) {
      if (row > 0 && row < 5) {
        return [10, 30, 60, 100][row - 1];
      } else {
        return 0;
      }
    }

    function go() {
      if (!fall) {
        return;
      }

      if (!overflow(shapeX, shapeY + 1)) {
        shapeY++;
        update();
        timerId = setTimeout(go, duration);
      } else {
        fall = false;
        copyCurrentShapeToCubes();
        score += rowToScore(clearRows());
        level = getLevelFromScore(score);
        duration = getDurationFromLevel(level);
        shape = null;
        if (level > MAX_LEVEL) {
          level = MAX_LEVEL;
          update();
          drawSuccess();
        } else {
          update();
          timerId = setTimeout(() => {
            shapeX = 3;
            shapeY = 0;
            shape = randomShape();
            if (!overflow(shapeX, shapeY)) {
              fall = true;
              go();
            } else {
              over = true;
              update();
            }
          }, duration);
        }
      }
    }

    document.onkeydown = function onKeyDown(event) {
      if (!fall) {
        return;
      }

      if (event.key === 'ArrowLeft') {
        if (!overflow(shapeX - 1, shapeY)) {
          shapeX--;
          update();
        }
      } else if (event.key === 'ArrowRight') {
        if (!overflow(shapeX + 1, shapeY)) {
          shapeX++;
          update();
        }
      } else if (event.key === 'ArrowDown') {
        if (!overflow(shapeX, shapeY + 1)) {
          shapeY++;
          update();
        }
      }
    }

    document.onkeyup = function onKeyUp(event) {
      if (!fall) {
        return;
      }

      if (event.key === 'ArrowUp') {
        var origin = shape;
        if (shape.length === 2) {
          if (shape[0] === 'T' || shape[0] === 'L' || shape[0] === 'J') {
            if (Number(shape[1]) < 4) {
              shape = shape[0] + (Number(shape[1]) + 1);
            } else {
              shape = shape[0] + 1;
            }
          } else if (shape[0] === 'I' || shape[0] === 'S' || shape[0] === 'Z') {
            if (shape[1] === '1') {
              shape = shape[0] + 2;
            } else {
              shape = shape[0] + 1;
            }
          }
          if (!overflow(shapeX, shapeY)) {
            update();
          } else {
            shape = origin;
          }
        }
      }
    }

    function onClickStart(event) {
      if (timerId) {
        clearTimeout(timerId);
        timerId = null;
      }

      event.target.innerText = '重新开始';
      score = 0;
      shapeX = 3;
      shapeY = 0;
      level = 1;
      duration = getDurationFromLevel(level);
      shape = randomShape();
      fall = true;
      shapeY--;
      cubes = createEmptyCubes();
      over = false;
      go();
    }

    function onClickPause(event) {
      if (timerId) {
        clearTimeout(timerId);
        timerId = null;
      }

      if (over) {
        return;
      }

      if (event.target.innerText === '暂停') {
        if (fall) {
          event.target.innerText = '继续';
          fall = false;
          drawPause();
        }
      } else {
        event.target.innerText = '暂停';
        fall = true;
        go();
      }
    }

    drawStart();
  </script>
</body>
</html>

 

转载于:https://my.oschina.net/u/3288561/blog/2050993

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值