HTML5 实现的一个俄罗斯方块实例代码

示例简单,运行地址为:http://chendd.cn/demo/html/canvas/elsfk.html,得需要支持html5浏览器的环境。

实现的功能:方块旋转(W键)、自动下落、移动(ASD)、消行、快速下落(空格键)、下落阴影、游戏结束。

为实现功能:消行时的计分、等级、以及不同等级的下落速度等。

学习了xiaoE的Java版本的俄罗斯方块后,自己动手使用html5的canvas实现的,

参考效果图如下:

详细代码如下:

<!DOCTYPE html>

<html>

 <head>

 <meta charset="utf-8">

 <title>俄罗斯方块</title>

 <style type="text/css">
  /*整个画布*/

  #tetris {
  border: 6px solid grey;
  }
  /*游戏面板*/
 </style>

 </head>

 <body>

 <canvas id="tetris" width="565" height="576"></canvas>

 <script type="text/javascript">
  var canvas = document.getElementById("tetris");
  var context = canvas.getContext("2d");
  var padding = 6,
  size = 32,
  minX = 0,
  maxX = 10,
  minY = 0,
  maxY = 18,
  score = 0,
  level = 1;
  var gameMap = new Array(); //游戏地图,二维数组
  var gameTimer;
  initGameMap();
  //绘制垂直线条
  drawGrid();
  var arrays = basicBlockType();
  var blockIndex = getRandomIndex();
  //随机画一个方块意思意思
  var block = getPointByCode(blockIndex);
  context.fillStyle = getBlockColorByIndex(blockIndex);
  drawBlock(block);
  /**

  * 初始化游戏地图

  */
  function initGameMap() {
  for (var i = 0; i < maxY; i++) {
   var row = new Array();
   for (var j = 0; j < maxX; j++) {
   row[j] = false;
   }
   gameMap[i] = row;
  }
  }
  /**

  * 方块旋转

  * 顺时针:

  * A.x =O.y + O.x - B.y

  * A.y =O.y - O.x + B.x

  */
  function round() {
  //正方形的方块不响应旋转
  if (blockIndex == 4) {
   return;
  }
  //循环处理当前的方块,找新的旋转点
  for (var i = 1; i < block.length; i++) {
   var o = block[0];
   var point = block[i];
   //旋转后的位置不能与现有格子的方块冲突
   var tempX = o.y + o.x - point.y;
   var tempY = o.y - o.x + point.x;
   if (isOverZone(tempX, tempY)) {
   return; //不可旋转
   }
  }
  clearBlock();
  //可以旋转,设置新的旋转后的坐标
  for (var i = 1; i < block.length; i++) {
   var o = block[0];
   var point = block[i];
   //旋转后的位置不能与现有格子的方块冲突
   var tempX = o.y + o.x - point.y;
   var tempY = o.y - o.x + point.x;
   block[i] = {
   x: tempX,
   y: tempY
   };
  }
  drawBlock();
  }
  function moveDown() {

  var overFlag = canOver();
  if(overFlag){
   //如果不能向下移动了,将当前的方块坐标载入地图
   window.clearInterval(gameTimer);
   add2GameMap();
   //清除游戏区域内的不同颜色的格子,使用单一颜色重新绘制地图堆积物
   redrawGameMap();
   return;//游戏结束
  }

  var flag = moveTo(0, 1);
  //如果可以移动,则继续移动
  if (flag) {
   return;
  }
  //如果不能向下移动了,将当前的方块坐标载入地图
  add2GameMap();

  //进行消行动作
  clearLines();
  //清除游戏区域内的不同颜色的格子,使用单一颜色重新绘制地图堆积物
  redrawGameMap();
  //如果不能向下移动,则继续下一个方块
  nextBlock();
  }

  /**

  * 消行动作,返回消除的行数

  */
  function clearLines() {
  var clearRowList = new Array();
  for (var i = 0; i < maxY; i++) {
   var flag = true;
   for (var j = 0; j < maxX; j++) {
   if (gameMap[i][j] == false) {
    flag = false;
    break;
   }
   }
   if (flag) {
   clearRowList.push(i); //记录消除行号的索引
   }
  }
  var clearRows = clearRowList.length;
  //所谓的消行就是将待消除行的索引,下方所有的格子上移动
  for (var x = 0; x < clearRows; x++) {
   var index = clearRowList[x];
   for (var i = index; i > 0; i--) {
   for (var j = 0; j < maxX; j++) {
    gameMap[i][j] = gameMap[i - 1][j];
   }
   }
  }
  if (clearRows > 0) {
   for (var i = 0; i < maxY; i++) {
   //此处可以限制满足相关条件的方块进行清除操作&& j < clearRowList[clearRows - 1]
   for (var j = 0; j < maxX; j++) {
    if (gameMap[i][j] == false) {
    clearBlockByPoint(i, j);
    }
   }
   }
  }
  }
  /**

  * 重绘游戏地图

  */
  function redrawGameMap() {
  drawGrid();
  for (var i = 0; i < maxY; i++) {
   for (var j = 0; j < maxX; j++) {
   if (gameMap[i][j]) {
    roadBlock(j, i);
   }
   }
  }
  }
  /**

  * 打印阴影地图

  */
  function drawShadowBlock() {
  var currentBlock = block;
  var shadowPoints = getCanMoveDown();
  if (shadowPoints != null && shadowPoints.length > 0) {
   for (var i = 0; i < shadowPoints.length; i++) {
   var point = shadowPoints[i];
   if (point == null) {
    continue;
   }
   var start = point.x * size;
   var end = point.y * size;
   context.fillStyle = "#abcdef";
   context.fillRect(start, end, size, size);
   context.strokeStyle = "black";
   context.strokeRect(start, end, size, size);
   }
  }
  }
  /**

  * 返回最多可移动到的坐标位置(统计总共可以下落多少步骤)

  * @return最多可移动到的坐标位置

  */
  function getCanMoveDown() {
  var nps = canMove(0, 1, block);
  var last = null;
  if (nps != null) {
   last = new Array();
   while ((nps = canMove(0, 1, nps)) != null) {
   if (nps != null) {
    last = nps;
   }
   }
  }
  return last;
  }

  function canOver(){
  var flag = false;
  for (var i = 0; i < block.length; i++) {
   var point = block[i];
   var x = point.x;
   var y = point.y;
   if(isOverZone(x , y)){
   flag = true;
   break;
   }
  }
  return flag;
  }

  function drawLevelScore() {

  }
  /**

  * 将不能移动的各种填充至地图

  */
  function add2GameMap() {
  for (var i = 0; i < block.length; i++) {
   var point = block[i];
   var x = point.x;
   var y = point.y;
   var gameMapRow = gameMap[y]; //获取到地图的一行
   gameMapRow[x] = true; //将此行中的某个格子标记为堆积物
   gameMap[y] = gameMapRow; //再将行给设置回来
  }
  }
  function moveLeft() {
  moveTo(-1, 0);
  }
  function moveRight() {
  moveTo(1, 0);
  }
  function quickDown() {
  while (moveTo(0, 1));
  }
  function moveTo(moveX, moveY) {
  var move = canMove(moveX, moveY, block); //判定是否可以移动
  if (move == null) {
   return false;
  }
  clearBlock();
  for (var i = 0; i < block.length; i++) {
   var point = block[i];
   point.x = point.x + moveX;
   point.y = point.y + moveY;
  }
  drawBlock();
  return true;
  }
  /**

  * 下一个方块

  */
  function nextBlock() {
  blockIndex = getRandomIndex();
  block = getPointByCode(blockIndex);
  context.fillStyle = getBlockColorByIndex(blockIndex);
  drawBlock();
  }
  document.onkeypress = function(evt) {
  var key = window.event ? evt.keyCode : evt.which;
  switch (key) {
   case 119: //向上旋转 W
   round();
   break;
   case 115: //向下移动 S
   moveDown();
   break;
   case 97: //向左移动 A
   moveLeft();
   break;
   case 100: //向右移动 D
   moveRight();
   break;
   case 32: //空格键快速下落到底
   quickDown();
   break;
  }
  }
  /**

  * 判定是否可以移动

  * @parammoveX 横向移动的个数

  * @parammoveY 纵向移动的个数

  */
  function canMove(moveX, moveY, currentBlock) {
  var flag = true;
  var newPoints = new Array();
  for (var i = 0; i < currentBlock.length; i++) {
   var point = currentBlock[i];
   var tempX = point.x + moveX;
   var tempY = point.y + moveY;
   if (isOverZone(tempX, tempY)) {
   flag = false;
   break;
   }
  }
  if (flag) {
   for (var i = 0; i < currentBlock.length; i++) {
   var point = currentBlock[i];
   var tempX = point.x + moveX;
   var tempY = point.y + moveY;
   newPoints[i] = {
    x: tempX,
    y: tempY
   };
   }
   return newPoints;
  }
  return null;
  }
  /**

  * 判定是否可以移动

  * @paramx 预移动后的横坐标

  * @paramy 预移动后的纵坐标

  */
  function isOverZone(x, y) {
  return x < minX || x >= maxX || y < minY || y >= maxY || gameMap[y][x];
  }
  document.body.click();

  gameTimer = window.setInterval(moveDown , 800);

  /**

  * 初始化方块的基础数据

  */
  function basicBlockType() {
  var arrays = new Array();
  arrays[0] = [{
   x: 4,
   y: 0
  }, {
   x: 3,
   y: 0
  }, {
   x: 5,
   y: 0
  }, {
   x: 6,
   y: 0
  }];
  arrays[1] = [{
   x: 4,
   y: 0
  }, {
   x: 3,
   y: 0
  }, {
   x: 5,
   y: 0
  }, {
   x: 4,
   y: 1
  }];
  arrays[2] = [{
   x: 4,
   y: 0
  }, {
   x: 3,
   y: 0
  }, {
   x: 5,
   y: 0
  }, {
   x: 3,
   y: 1
  }];
  arrays[3] = [{
   x: 4,
   y: 0
  }, {
   x: 5,
   y: 0
  }, {
   x: 3,
   y: 1
  }, {
   x: 4,
   y: 1
  }];
  arrays[4] = [{
   x: 4,
   y: 0
  }, {
   x: 5,
   y: 0
  }, {
   x: 4,
   y: 1
  }, {
   x: 5,
   y: 1
  }];
  arrays[5] = [{
   x: 4,
   y: 0
  }, {
   x: 3,
   y: 0
  }, {
   x: 5,
   y: 0
  }, {
   x: 5,
   y: 1
  }];
  arrays[6] = [{
   x: 4,
   y: 0
  }, {
   x: 3,
   y: 0
  }, {
   x: 4,
   y: 1
  }, {
   x: 5,
   y: 1
  }];
  return arrays;
  }
  function basicBlockColor() {
  return ["#A00000", "#A05000", "#A0A000", "#00A000", "#00A0A0", "#0000A0", "#A000A0"];
  }
  function getBlockColorByIndex(typeCodeIndex) {
  var arrays = basicBlockColor();
  return arrays[typeCodeIndex];
  }
  /**

  * 根据编号返回指定编号的方块

  * @paramtypeCodeIndex 方块编号索引

  */
  function getPointByCode(typeCodeIndex) {
  var arrays = basicBlockType();
  return arrays[typeCodeIndex];
  }
  /**

  * 获取随即出现方块的范围值

  * @paramlens 随机数的范围

  */
  function getRandomIndex() {
  return parseInt(Math.random() * (arrays.length - 1), 10);
  }
  /**

  * 绘制方块,按格子单个绘制

  */
  function drawBlock() {
  drawGrid();
  for (var i = 0; i < block.length; i++) {
   var point = block[i];
   var start = point.x * size;
   var end = point.y * size;
   context.fillStyle = getBlockColorByIndex(blockIndex);
   context.fillRect(start, end, size, size);
   context.strokeStyle = "black";
   context.strokeRect(start, end, size, size);
  }
  drawShadowBlock();
  }
  /**

  * 绘制障碍物

  */
  function roadBlock(x, y) {
  context.fillStyle = "darkgray";
  var start = x * size;
  var end = y * size;
  context.fillRect(start, end, size, size);
  }
  /**

  * 绘制新的方块先清除之前的方块

  */
  function clearBlock() {
  for (var i = 0; i < block.length; i++) {
   var point = block[i];
   var start = point.x * size;
   var end = point.y * size;
   context.clearRect(start, end, size, size);
  }
  }
  /**

  * 初始化一个新的行

  */
  function initGameMapRow() {
  var array = new Array();
  for (var i = 0; i < maxX; i++) {
   array[i] = false;
  }
  return array;
  }
  /**

  * 根据坐标清除指定格子的内容

  * @paramx 横坐标

  * @paramy 纵坐标

  */
  function clearBlockByPoint(x, y) {
  var start = y * size;
  var end = x * size;
  context.clearRect(start, end, size, size);
  }
  /**

  * 清掉所有位置的空白格的绘图

  */
  function clearAllNullPoint() {
  for (var i = 0; i < maxY; i++) {
   for (var j = 0; j < maxX; j++) {
   if (gameMap[i][j] == false) {
    clearBlockByPoint(i, j);
   }
   }
  }
  }
  /**

  * 绘制网格线

  * @paramcontext 绘图对象

  */
  function drawGrid() {
  clearAllNullPoint(); //清除掉当前方块下落位置造成的阴影
  context.strokeStyle = "grey"; //画笔颜色
  for (var i = 0; i <= maxX; i++) {
   var start = i * size;
   var end = start + size;
   context.beginPath();
   context.moveTo(start, 0);
   context.lineTo(size * i, size * maxY);
   context.stroke();
   context.closePath();
  }
  //绘制水平线条
  for (var i = 0; i <= maxY; i++) {
   var start = i * size;
   var end = start + size;
   context.beginPath();
   context.moveTo(0, size * i);
   context.lineTo(size * maxX, size * i);
   context.stroke();
   context.closePath();
  }
  }
 </script>

 </body>

</html>

以上就是HTML5 实现的一个俄罗斯方块的实例,有兴趣的小伙伴可以参考下,谢谢大家对本站的支持!

(0)

相关推荐

  • 手机端 HTML5使用photoswipe.js仿微信朋友圈图片放大效果

    先来几张效果图: 点击其中一张照片可放大,可支持图片文字描述: 同时支持分享功能: 支持手势放大缩小 使用js框架是PhotoSwipe.  PhotoSwipe是一个图片放大插件,兼容pc和移动端,经历过多个版本的迭代且一直在不断更新,踩过的坑不知凡几,在移动端有着巨大的优势. 1.可控制多种风格如: 标题.分享.全屏按钮,点击事件.是否加入字幕,背景透明等. 2.可支持移动端触摸手势兼容pc端 所有的基本手势支持:滑动下一个或上一个,拖动平移.缩放.放大或关闭,点击切换控件,双击放大或缩放.

  • HTML5 移动页面自适应手机屏幕宽度详解

    网上关于这方面的文章有很多,重复的东西本文不再赘述,仅提供思路,并解释一些其他文章讲述模糊的地方. 1.使用meta标签,这也是普遍使用的方法,理论上讲使用这个标签是可以适应所有尺寸的屏幕的,但是各设备对该标签的解释方式及支持程度不同造成了不能兼容所有浏览器或系统. 首先解释该标签的含义: <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, max

  • 正则表达式与HTML5新元素

    ProcessOn是一个在线协作绘图平台,为用户提供最强大.易用的作图工具! 它可以很方便的在线简单绘制一些东西,让自己去具体理解. 正则表达式 今日的正则只是学习正则的简单使用.在HTML5的标签属性的强大面前,我们已经可以不用去先学习javascript才能再去正则了,因为HTML5的input标签的pattern可以实现标签的验证,近似取代复杂的javascript使用正则验证表单,在未来一定会完全取代的. 正则的简单使用: [0-9] 查找任何从 0 至 9 的数字. [a-z] 查找任

  • JS+HTML5手机开发之滚动和惯性缓动实现方法分析

    本文实例讲述了JS+HTML5手机开发之滚动和惯性缓动实现方法.分享给大家供大家参考,具体如下: 1. 滚动 以下是三种实现方式: 1) 利用原生的css属性 overflow: scroll div id= parent style = overflow:scroll; divid='content'内容区域/div /div Notice: 在android 有bug, 滚动完后会回退到最顶端的内容区域,解决办法是使用后两种方式实现 2)js 编程实现 思路:对比手指在屏幕上移动前后位置变化

  • HTML5 canvas 9绘制图片实例详解

    绘制图片 Var image=new Image(); image.src=" http://img4.duitang.com/uploads/item/201406/25/20140625182321_4MTau.thumb.700_0.jpeg"; image.onload=function(){} Context.drawImage(image,x,y); Context.drawImage(image,x,y,w,h); Context.drawIamge(image,sx,s

  • js HTML5多图片上传及预览实例解析(不含前端的文件分割)

    本文实例为大家分享了js HTML5多图片上传及预览实例,供大家参考,具体内容如下 主要运用  1.HTML5 files可以选择多文件,以及获取多文件信息  2.XMLHTTPRequest对象,发送HTTP传输请求  3.将每一个文件放在FormData,进行传输  4.利用readAsDataURL将图片内容直接变成url,放在img标签的src当中,生成可预览的图片 html+css+js代码 <!DOCTYPE html> <head> <meta http-equ

  • NodeJS与HTML5相结合实现拖拽多个文件上传到服务器的实现方法

    实现多文件拖拽上传的简易Node项目,可以在github上下载,你可以先下载下来:https://github.com/Johnharvy/upLoadFiles/. 解开下载下的zip格式包,建议用webstom 运行该项目,通过app.js启动项目,如果提示找不到node.exe执行环境,请指定好你的node.exe安装位置.这里我用的express框架是3.21.2版本. 我们来简单介绍下拖拽效果是怎么实现的. 这里先看代码: <!DOCTYPE html> <html lang=

  • 使用HTML5+Boostrap打造简单的音乐播放器

    前言:这个是综合一下我最近在学的东西做的小Demo,到实际使用还有距离,但是用来练手巩固知识点还是不错的,最近在二刷JS书和Boostrap.css的源码,做完这个Demo也算是暂告一段落,接下来是jQuery的源码和Boostrap.js的源码,任务很艰巨呢,加油~在此就不整篇的贴代码了,如果感兴趣的小伙伴可以发消息给我,可以把代码传给你们~ 正文: 先上效果图 1.布局:Boostrap里的响应式和自适应布局是自然跑不掉的,container中嵌套row再分别嵌套aside和main(H5新

  • 关于Android HTML5 audio autoplay无效问题的解决方案

    前言:在android HTML5 开发中有不少人遇到过 audio 标签 autoplay在某些设备上无效的问题,网上大多是讲怎么在js中操作,即在特定的时刻调用audio的play()方法,在android上还是无效. 一.解决方案 在android 4.2添加了允许用户手势触发音视频播放接口,该接口默认为 true ,即默认不允许自动播放音视频,只能是用户交互的方式由用户自己促发播放. WebView webView = this.finishActivity(R.id.main_act_

  • HTML5 实现的一个俄罗斯方块实例代码

    示例简单,运行地址为:http://chendd.cn/demo/html/canvas/elsfk.html,得需要支持html5浏览器的环境. 实现的功能:方块旋转(W键).自动下落.移动(ASD).消行.快速下落(空格键).下落阴影.游戏结束. 为实现功能:消行时的计分.等级.以及不同等级的下落速度等. 学习了xiaoE的Java版本的俄罗斯方块后,自己动手使用html5的canvas实现的, 参考效果图如下: 详细代码如下: <!DOCTYPE html> <html> &

  • Android 画一个太极图实例代码

    今天练手一下,一起来画个太极图吧~ 最终效果如下: 最终效果 一般都是先讲原理,我就反其道而行,先讲实现吧. 1.继承实现初始化方法 继承View,实现基本的构造函数: public TestView(Context context) { this(context, null); } public TestView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public TestView(Context c

  • JS+canvas画一个圆锥实例代码

    以下是我们给大家分享是实例代码: <html> <head> <title>我的第一个 HTML 页面</title> </head> <body> <canvas id='cvs' width='1000' height="800"> </canvas> <script> const cvs =document.getElementById('cvs'); // 计算画布的宽度

  • 利用kotlin实现一个饼图实例代码

    前言 饼图是许多人最熟悉的图表类型,也是使用频率最高的图表类型之一,本文主要给大家介绍了关于利用kotlin实现饼图的相关内容,分享出来供大家参考学习,代码不难,所以打算用kotlin来实现,增加熟练度,下面来一起看看吧. 先看看做的是什么 看完图,我们来整理下思路 饼图居中,每块区域都是一个扇形,需要canvas.drawArc根据角度来绘制 需要path.arcTo定位到扇形弧度的一半来绘制折线的起点 通过canvas.drawPath绘制折线,折线的长度根据饼图大小来设置比例 通过canv

  • html5 canvas js(数字时钟)实例代码

    复制代码 代码如下: <!doctype html><html>    <head>        <title>canvas dClock</title>    </head>    <body>        <canvas id = "clock" width = "500px" height = "200px">            您的浏览

  • 如何利用vue3实现一个俄罗斯方块

    目录 前言 游戏相关设置 游戏界面设置 存储还在移动的俄罗斯方块信息 存储已经不能移动的俄罗斯方块信息 使用之前在贪吃蛇中使用的颜色渲染工具 让方块移动起来(不考虑切换方块的形态切换) 检测移动的俄罗斯方块二维数组在移动后它的每个坐标点数组是否有超出范围的 检测移动的俄罗斯方块二维数组在移动后它的每个坐标点数组是否会触碰到不可移动的俄罗斯方块上 特殊情况(得分) 移动方法 切换操作书写 设计检测该形状形态的方法 检测z形和反z形形态的方法 检测L形.反L形.三角形形态的方法 设计切换方法 切换L

  • 使用Python写一个贪吃蛇游戏实例代码

    我在程序中加入了分数显示,三种特殊食物,将贪吃蛇的游戏逻辑写到了SnakeGame的类中,而不是在Snake类中. 特殊食物: 1.绿色:普通,吃了增加体型 2.红色:吃了减少体型 3.金色:吃了回到最初体型 4.变色食物:吃了会根据食物颜色改变蛇的颜色 #coding=UTF-8 from Tkinter import * from random import randint import tkMessageBox class Grid(object): def __init__(self,

  • JS判断输入字符串长度实例代码(汉字算两个字符,字母数字算一个)

    js判断输入字符串长度实例代码(汉字算两个字符,字母数字算一个) 文本输入时,由于数据库表字段长度限制会导致提交失败,因此想到了此方法验证. 废话不多说上代码: <html> <head> <title>js判断输入字符串长度(汉字算两个字符,字母数字算一个)</title> <style type="text/css"> .pbt { margin-bottom: 10px; } .ie6 .pbt .ftid a, .ie

  • 一个简单的php MVC留言本实例代码(必看篇)

    摘要 标题上我把这个留言板叫最简单的,其实应该叫最简陋的,因为把全部注意力集中在MVC模式设计和实现上,所以UI方面几乎没有一点修饰. 这个小程序一共包含6个文件,其中index.php是程序入口.post.htm是留言表单.在lib文件夹里Model.View .Controller三个文件分别实现MVC,DataAccess是一个简单的数据库访问类.其实这个程序是国外的一个人写的. PHP代码: /** * 一个用来访问MySQL的类 * 仅仅实现演示所需的基本功能,没有容错等 * 代码未作

  • 用python做一个搜索引擎(Pylucene)的实例代码

    1.什么是搜索引擎? 搜索引擎是"对网络信息资源进行搜集整理并提供信息查询服务的系统,包括信息搜集.信息整理和用户查询三部分".如图1是搜索引擎的一般结构,信息搜集模块从网络采集信息到网络信息库之中(一般使用爬虫):然后信息整理模块对采集的信息进行分词.去停用词.赋权重等操作后建立索引表(一般是倒排索引)构成索引库:最后用户查询模块就可以识别用户的检索需求并提供检索服务啦. 图1 搜索引擎的一般结构 2. 使用python实现一个简单搜索引擎 2.1 问题分析 从图1看,一个完整的搜索

随机推荐