JS和canvas实现俄罗斯方块


Posted in Javascript onMarch 14, 2017

效果图:

JS和canvas实现俄罗斯方块

代码如下:

<!DOCTYPE html>
<html>
 <head>
 <meta charset="UTF-8">
 <title></title>
 </head>
 <body>
 <p style="font-size: 20px;">键盘箭头A,D控制左右移动,S控制快速下滑。W控制变形,空格控制暂停,enter开始游戏</p>
 <div id="box" style="width: 1000px;height: 800px;border: 1px solid pink;margin: 0 auto;position: relative;">
 </div>
 </body>
<script type="text/javascript">
shape_collection = {//图形顺时针旋转变形,感觉有点麻烦,这里还应该加一个参数--距离左右多少格的时候不能变形,有利于后续图形的拓展,但是这里就先不写了
 s1: [
 {
  mo: [
  [0, 0, 0, 0],
  [0, 0, 0, 0],
  [0, 0, 0, 0],
  [1, 1, 1, 1]
  ],
  l:true,//通过这两个参数,判断左右碰撞能否变形
  r:true
 },
 {//与左边,右边碰撞,都不能变形(距离右边一格的时候,也不能变形)
  mo: [
  [0, 1, 0, 0],
  [0, 1, 0, 0],
  [0, 1, 0, 0],
  [0, 1, 0, 0]
  ] ,
  l:false,
  r:false
 }
 ],
 s2: [
 {
  mo: [//始终不变形
  [1, 1, 0, 0],
  [1, 1, 0, 0],
  [0, 0, 0, 0],
  [0, 0, 0, 0]
  ],
  l:false,
  r:false
 }
 ],
 s3: [
 {
  mo: [//右边不能变形
  [1, 0, 0, 0],
  [1, 1, 0, 0],
  [0, 1, 0, 0],
  [0, 0, 0, 0]
  ],
  l:true,
  r:false
 },
 {
  mo: [
  [0, 0, 0, 0],
  [0, 1, 1, 0],
  [1, 1, 0, 0],
  [0, 0, 0, 0]
  ],
  l:true,
  r:true
 }
 ],
 s4: [
 {
  mo: [//右边不能变形
  [0, 1, 0, 0],
  [1, 1, 0, 0],
  [1, 0, 0, 0],
  [0, 0, 0, 0]
  ],
  l:true,
  r:false
 },
 {
  mo: [
  [0, 0, 0, 0],
  [1, 1, 0, 0],
  [0, 1, 1, 0],
  [0, 0, 0, 0]
  ],
  l:true,
  r:true
 }
 ],
 s5: [
 {
  mo: [//右边不能变形,左边可以变形
  [1, 0, 0, 0],
  [1, 0, 0, 0],
  [1, 1, 0, 0],
  [0, 0, 0, 0]
  ],
  l:true,
  r:false
 },
 {
  mo: [//
  [0, 0, 0, 0],
  [1, 1, 1, 0],
  [1, 0, 0, 0],
  [0, 0, 0, 0]
  ],
  l:true,
  r:true
 },
 {
  mo: [//右边不能变形
  [1, 1, 0, 0],
  [0, 1, 0, 0],
  [0, 1, 0, 0],
  [0, 0, 0, 0]
  ],
  l:true,
  r:false
 },
 {
  mo: [
  [0, 0, 0, 0],
  [0, 0, 1, 0],
  [1, 1, 1, 0],
  [0, 0, 0, 0]
  ],
  l:true,
  r:true
  }
 ],
 s6: [
 {
  mo: [//右边不能变形,左边可以变形
  [0, 1, 0, 0],
    [0, 1, 0, 0],
    [1, 1, 0, 0],
    [0, 0, 0, 0]
  ],
  l:true,
  r:false
  },
 {
  mo: [//
  [0, 0, 0, 0],
  [1, 0, 0, 0],
  [1, 1, 1, 0],
  [0, 0, 0, 0]
  ],
  l:true,
  r:true
 },
 {
  mo: [//右边不能变形
  [1, 1, 0, 0],
  [1, 0, 0, 0],
  [1, 0, 0, 0],
  [0, 0, 0, 0]
  ],
  l:true,
  r:false
 },
 {
  mo: [
  [0, 0, 0, 0],
  [1, 1, 1, 0],
  [0, 0, 1, 0],
  [0, 0, 0, 0]
  ],
  l:true,
  r:true
 }
 ],
 s7: [
 {
  mo: [//
  [0, 0, 0, 0],
    [0, 1, 0, 0],
    [1, 1, 1, 0],
    [0, 0, 0, 0]
  ],
  l:true,
  r:true
 },
 {
  mo: [//右边不能变形
  [1, 0, 0, 0],
  [1, 1, 0, 0],
  [1, 0, 0, 0],
  [0, 0, 0, 0]
  ],
  l:true,
  r:false
 },
 {
  mo: [//
  [0, 0, 0, 0],
  [1, 1, 1, 0],
  [0, 1, 0, 0],
  [0, 0, 0, 0]
  ],
  l:true,
  r:true
 },
 {
  mo: [//右边不能变形
  [0, 1, 0, 0],
  [1, 1, 0, 0],
  [0, 1, 0, 0],
  [0, 0, 0, 0]
  ],
  l:true,
  r:false
 }
 ],
}
</script>
<script type="text/javascript">
;(function(){
 var tetris=function(a){
 var _this=this;
 this.settings={
  dom:"",
  side_length:25,//方块边长
  row:20,//行数
  col:15,//列数
  v_dowm:10,
  side_width:1//分隔线条宽度默认为1
 };
 var k=this.extend(a,this.settings);
 var dom=document.getElementById(this.settings.dom);
 var fall_2=undefined;//用于判断快速下落
 var score=0;//分数
 this.p_dom(dom,"初始级别:");
 this.p_dom(dom,"分数:","score");
 var score_dom=dom.getElementsByClassName("score")[0];
 score_dom.style.cssText="position:absolute;left: 0;right: 0;top: 30px;margin: auto auto;text-align: center;";
 var score_num=document.createElement("span");
 score_num.className="score_num";
 score_num.style.cssText="color:gold;font-size: 30px;";
 score_num.innerText=score;
 score_dom.appendChild(score_num);
 this.input_dom(dom,4);
 var start_con=false;//是否开始游戏
 var shape =shape_collection;//各种形状,这是一种思路,也很利于以后形状的添加
 var shape_key=_this.obj_list(shape);//[s1,s2,s3,s4,s5,s6,s7]
 var color=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"];//颜色
 var cube_arr=[];//用来装建造区域小方块
 var cube_arr_s=[];//用来装提示图形区域小方块
 var shape_arr={//用来表示下落的滑块
  shape_solo:undefined,//s1——s7中某一个的某一个图形
  solo_key:undefined,//shape_solo的key值
  mo_list:undefined,//绘制的图形在数组shape_solo的下标,变换图形的时候用到
  serial:[],//小方块坐标集合,默认为空,不绘制小块。绘制小方块的参考,重要参数
  re_point_x:(_this.settings.col/2)|0,//以第几个为初始参考X坐标(用于左右移动)
  re_point_y:0,//y坐标参考。(不要在serial里面修改值,serial总在4X4里面,容易做参考)
  width:_this.settings.side_length,//小方块的宽度
v:_this.settings.side_length+_this.settings.side_width,//每次移动的距离等于方块边长+grid线宽
  collision_l:false,//与左边是否碰撞
  collision_r:false,//与右边是否碰撞
  collision_d:false,//与下边是否碰撞
  draw:function(a){//绘制小方块函数,写在这里,才是这个对象的私有方法,而_proto_的写法是所有对象都拥有
   var se=this.serial;
   //console.log(se)
   for(var i=0;i<se.length;i++){
   a.beginPath();
   a.fillStyle=se[0][2];//this指向这个对象.第三个参数是颜色
   a.fillRect(this.v*(this.re_point_x+se[i][0]),this.v*(this.re_point_y+se[i][1]),this.width,this.width);
   }
  }
 };
 //创建提示图形
 var ts="ts";//提示图形ID;
 this.canvas_dom(dom,this.settings.side_length,4,4,this.settings.side_width);
 var canvas_s=dom.getElementsByTagName("canvas")[0];
 canvas_s.setAttribute("style","position: absolute;right: 130px;border:3px solid goldenrod;top: 82px;")
 var ctx_s=canvas_s.getContext("2d");
 var cube_obj_s=_this.cube_obj_arr(ctx_s,this.settings.side_length,4,4,this.settings.side_width,cube_arr_s);
 var shape_arr_s=this.clone_obj(this.draw_shape(ts,shape,shape_arr,shape_key,_this.make_color(color)));//因为返回的是shape_arr,所以这里导致了shape_arr_s和shape_arr有关联,也就是说以后的代码中,其中一个改变的话,另一个也会跟着改变,用clone方法去掉关联
 cube_obj_s.forEach(function(e){
  e.draw(ctx_s);
 });
 shape_arr_s.draw(ctx_s);
 //创建堆砌图形
 var dq="dq";//堆砌图形ID;
 this.canvas_dom(dom,this.settings.side_length,this.settings.row,this.settings.col,this.settings.side_width);
 var canvas=dom.getElementsByTagName("canvas")[1];
 var ctx=canvas.getContext("2d");
 var cube_obj=_this.cube_obj_arr(ctx,this.settings.side_length,this.settings.row,this.settings.col,this.settings.side_width,cube_arr);
 //this.grid(ctx,this.settings.side_length,this.settings.row,this.settings.col,this.settings.side_width);//网格
 //console.log(cube_obj);
 cube_obj.forEach(function(e){
  e.draw(ctx);
 });
 var shape_arr_t=this.clone_obj(this.draw_shape(ctx,shape,shape_arr,shape_key,_this.make_color(color)));
 //console.log(shape_arr);
 shape_arr_t.draw(ctx);
 //鼠标事件
 window.onkeydown=function(e){
  switch (e.keyCode){
  case 87://up
   up();
   break;
  case 83://down
   //down();
   break;
  case 65://left
   left();
   break; 
  case 68://right
   right()
   break;
  case 32://workspace
   //up();
   break; 
  case 13://enter
   enter();
   break; 
  default:
   break;
  }
 };
 window.onkeypress=function(e){//onkeypress和onkeydown,即使按下的是同一个按键,e.keyCode也不一样
  if(start_con){ 
  switch (e.keyCode){
   case 115:
   if(fall_2==undefined){//做这个判断是为了防止快速的点击S键时,出现的多次fall_2事件,因为50ms之后,fall_2的值才变成undefined
    fall_2=setInterval(fall,10);//clearInterval返回的值是undefined,而setInterval返回的值是一个整数
    setTimeout(function(){
    fall_2=clearInterval(fall_2);//重置fall_2的值为undefined
    },50);
   }
   break;
   default:
   break;
  }
  }
 }
 function up(){//shape和shape_arr的关系要弄清楚,这里判断有点复杂
  if(start_con){
  ctx.clearRect(0,0,canvas.width,canvas.height);//清屏
  cube_obj.forEach(function(e){//重新绘制虚拟小方块
   e.draw(ctx);
  })
  var l=shape[shape_arr_t["solo_key"]].length-1;//数组长度减1
  var i_list=shape_arr_t["mo_list"];//对应数组的key
  var re_point_x=shape_arr_t["re_point_x"];
  var con_l=shape_arr_t["shape_solo"].l;
  var con_r=shape_arr_t["shape_solo"].r;
  var con_l_l=false;//用于判断左右是否有con为1的虚拟滑块,不仅仅要判断是否碰到边上,还要判断这个
  var con_r_r=false;
  var con_suxian=false;//竖线
  var arr_se=[];
  var k_uni=shape_arr_t["serial"].length;
  for(var i_uni=0;i_uni<k_uni;i_uni++){//取出图形所有小块的横坐标,这里有两个for循环,相同的变量会互相影响,所有设置成i_uni
   arr_se.push(shape_arr_t["serial"][i_uni][0]);
  }
  var min_coor=Math.min.apply(null,arr_se);
  var max_coor=Math.max.apply(null,arr_se);
  for(var i_y_a=0;i_y_a<k_uni;i_y_a++){
   var c_x_1=shape_arr_t["re_point_x"]+shape_arr_t["serial"][i_y_a][0];
   var c_y_1=shape_arr_t["re_point_y"]+shape_arr_t["serial"][i_y_a][1];
   if(cube_obj[c_y_1*_this.settings.col+c_x_1+1]!==undefined&&cube_obj[c_y_1*_this.settings.col+c_x_1+1].con==1){//任意滑块con==1的时候
   con_r_r=true;
   break;
   }
  }
  for(var i_y_b=0;i_y_b<k_uni;i_y_b++){//其实左右可以合并成一个判断,省掉一些代码
   var c_x_2=shape_arr_t["re_point_x"]+shape_arr_t["serial"][i_y_b][0];
   var c_y_2=shape_arr_t["re_point_y"]+shape_arr_t["serial"][i_y_b][1];
   if(cube_obj[c_y_2*_this.settings.col+c_x_2-1].con==1){//任意滑块con==1的时候
   con_l_l=true;
   break;
   }
  }
  if(shape_arr_t["solo_key"]==="s1"){//竖线情况特殊,单独处理,其实应该写一个函数来统一判断,不应该出现特殊图形,不然特殊图形增加的话,要增加很多判读  
   if(i_list==1){//如果是竖线的话
   for(var i_y_c=0;i_y_c<k_uni;i_y_c++){//如果是竖线的话,则右边2格处虚拟方块的con是1,也不能变形,这里到下一行的情况也能适应
    var c_x_3=shape_arr_t["re_point_x"]+shape_arr_t["serial"][i_y_c][0];
    var c_y_3=shape_arr_t["re_point_y"]+shape_arr_t["serial"][i_y_c][1];
    if(cube_obj[c_y_3*_this.settings.col+c_x_3+2]!==undefined&&cube_obj[c_y_3*_this.settings.col+c_x_3+2].con==1||cube_obj[c_y_3*_this.settings.col+c_x_3-1].con==1){//任意滑块con==1的时候
    con_suxian=true;
    break;
    }
   }
   if(re_point_x+max_coor<_this.settings.col-2&&re_point_x>(0- min_coor)&&!con_suxian){
    shape_arr_t["shape_solo"]=shape[shape_arr_t["solo_key"]][0];
    shape_arr_t["mo_list"]=0;//序号为0
   }
   }else{
   shape_arr_t["shape_solo"]=shape[shape_arr_t["solo_key"]][i_list+1];
   shape_arr_t["mo_list"]+=1;//序号加1
   }
  }else{
   if(re_point_x+max_coor>=(_this.settings.col-1)||con_r_r){//靠近右边或者右边有con为1的方快(实体方块)
   if(con_r){
    if(i_list<l){
    shape_arr_t["shape_solo"]=shape[shape_arr_t["solo_key"]][i_list+1];
    shape_arr_t["mo_list"]+=1;//序号加1
    }else{
    shape_arr_t["shape_solo"]=shape[shape_arr_t["solo_key"]][0];
    shape_arr_t["mo_list"]=0;//序号为0
    }
   }
   }else if(re_point_x<=(0-min_coor)||con_l_l){//靠近左边或者左边有con=1的方块
   if(con_l){
    if(i_list<l){
    shape_arr_t["shape_solo"]=shape[shape_arr_t["solo_key"]][i_list+1];
    shape_arr_t["mo_list"]+=1;//序号加1
    }else{
    shape_arr_t["shape_solo"]=shape[shape_arr_t["solo_key"]][0];
    shape_arr_t["mo_list"]=0;//序号为0
    }
   }
   }else{
   if(i_list<l){
    shape_arr_t["shape_solo"]=shape[shape_arr_t["solo_key"]][i_list+1];
    shape_arr_t["mo_list"]+=1;//序号加1
   }else{
    shape_arr_t["shape_solo"]=shape[shape_arr_t["solo_key"]][0];
    shape_arr_t["mo_list"]=0;//序号为0
   }
   }
  }
  var s_cube=shape_arr_t["shape_solo"];//图形变换,根据shape
  var s_mo=s_cube["mo"];//图形元素
  var k=s_mo.length;
  var color=shape_arr_t["serial"][0][2];//取出颜色
  //console.log(color)
  shape_arr_t["serial"].length=0;//数组置空
  //console.log(shape_arr["serial"])
  for(var i=0;i<k;i++){//这里的长度是4
   for(var j=0;j<k;j++){//这里的长度是4
   if(s_mo[i][j]){//如果等于1的话
    shape_arr_t["serial"].push([j,i,color])//需要绘制小块的坐标,所有小方块颜色一样,如果想变成不一样,则在for循环内部执行生成颜色函数,j,i才能对应图形
   }
   }
  }
  //console.log(shape_arr)
  shape_arr_t.draw(ctx);//重新绘制
  };
 }
 function left(){
  if(start_con){
  ctx.clearRect(0,0,canvas.width,canvas.height);//清屏
  cube_obj.forEach(function(e){//重新绘制虚拟小方块
   e.draw(ctx);
  })
  var arr_se=[];
  var con=true;
  var k=shape_arr_t["serial"].length;
  for(var i_uni=0;i_uni<k;i_uni++){//取出图形所有小块的横坐标
   arr_se.push(shape_arr_t["serial"][i_uni][0]);
  }
  var min_coor=Math.min.apply(null,arr_se);//取出最小的,其实这里除了竖线那个图形之外,最小的值都是0,但是这里这样写,有利于拓展以后可能出现的其他情况
  if(shape_arr_t["re_point_x"]<=(0- min_coor)){//碰到边界的时候
   con=false;
  }
  for(var i=0;i<k;i++){//判断所有小块左边的虚拟方块的con是否都是0
   var c_x=shape_arr_t["re_point_x"]+shape_arr_t["serial"][i][0];
   var c_y=shape_arr_t["re_point_y"]+shape_arr_t["serial"][i][1];
   if(cube_obj[c_y*_this.settings.col+c_x-1].con==1){//任意滑块con==1的时候。住:加1减一的时候,有可能到上一行下一行,但是这种情况就靠边了,所以这样判断还是对的
   con=false;
   break;
   }
  }
  if(con){
   shape_arr_t["re_point_x"]-=1; 
  }
  shape_arr_t.draw(ctx);//重新绘制
  }
 };
 function right(){
  if(start_con){
  ctx.clearRect(0,0,canvas.width,canvas.height);//清屏
  cube_obj.forEach(function(e){//重新绘制虚拟小方块
   e.draw(ctx);
  })
  var con=true;
  var arr_se=[];
  var k=shape_arr_t["serial"].length;
  for(var i_uni=0;i_uni<k;i_uni++){//取出图形所有小块的横坐标
   arr_se.push(shape_arr_t["serial"][i_uni][0]);
  }
  var max_coor=Math.max.apply(null,arr_se);//取出最大的
  if(shape_arr_t["re_point_x"]+max_coor>=(_this.settings.col-1)){//靠边的话
   con=false;
  }
  for(var i=0;i<k;i++){//判断所有小块右边的虚拟方块的con是否都是0
   var c_x=shape_arr_t["re_point_x"]+shape_arr_t["serial"][i][0];
   var c_y=shape_arr_t["re_point_y"]+shape_arr_t["serial"][i][1];
   if(cube_obj[c_y*_this.settings.col+c_x+1]!==undefined&&cube_obj[c_y*_this.settings.col+c_x+1].con==1){//任意滑块con==1的时候
   con=false;
   break;
   }
  }
  if(con){
   shape_arr_t["re_point_x"]+=1; 
  }
  shape_arr_t.draw(ctx);//重新绘制
  }
 };
 function down(){
 };
 function enter(){
  var radio_arr=document.getElementsByClassName("level");
  if(!start_con){//如果还没开始的话,则开始(//设置条件判断。不停的点击enter的时候,只执行一次)
  Array.prototype.forEach.call(radio_arr,function(e){
   if(e.checked==true){
   k_t=e.value;//用于级别增加
   level_t=e.value;
   }
   e.setAttribute("disabled",true);
  })
  fall_1=setInterval(fall,_this.settings.v_dowm/level_t)//全局变量,用于domn函数的清除,但是这样就不能避免外部的污染了
  start_con=true;
  }
 };
 function get_score(){//得分机制,写了很多for循环,感觉应该有更好的判断
  var lose_row=[];//消失的行数
  var clear_con=false;//是否当前需要重绘
  for(var i=0;i<_this.settings.row;i++){
  var all_con=true;//默认当前行所有滑块的con都是1
  for(var j=0;j<_this.settings.col;j++){
   if(cube_obj[i*_this.settings.col+j]["con"]!==1){
   all_con=false;
   break;
   }
  }
  if(all_con){
   lose_row.push(i)
  } 
  }//以下代码在没有消除的行的时候,都不执行
  if(lose_row.length>0){//计算分数
  switch (lose_row.length){
   case 1:
   score+=10;
   break;
   case 2:
   score+=30;
   break;
   case 3:
   score+=60
   break;
   case 4:
   score+=100;
   break; 
   default:
   break;
  }
  }
  for(var i_sc=0;i_sc<lose_row.length;i_sc++){//把需要消除行的con变为0,用于消除,length为0的时候,表示没有需要消除的行
  for(var j_sc=0;j_sc<_this.settings.col;j_sc++){
   cube_obj[lose_row[i_sc]*_this.settings.col+j_sc]["con"]=0;//重置为虚拟
   cube_obj[lose_row[i_sc]*_this.settings.col+j_sc]["fillStyle"]="pink"//颜色重新变成粉红,这是0的标示
  }
  clear_con=true;//只要有需要消除的行,就要重绘
  }
  if(clear_con){
  ctx.clearRect(0,0,canvas.width,canvas.height);//清屏
  cube_obj.forEach(function(e){//重新绘制虚拟小方块,移除消除的行
   e.draw(ctx);
  })
  score_num.innerText=score;//重新计算分数
  }
  for(var i_sc=0;i_sc<lose_row.length;i_sc++){//con为1的滑块往下移动
  var con_one=[];//用来存储所有con为1的虚拟滑块
  for(var i_v=0;i_v<_this.settings.row*_this.settings.col;i_v++){
   if(cube_obj[i_v]["con"]==1){
   con_one.push(cube_obj[i_v]);
   }
  }
  for(var i_l=con_one.length-1;i_l>=0;i_l--){//
   if(con_one[i_l]["row"]<lose_row[i_sc]){//如果位置在消失的滑块上方
   var fillStyle=con_one[i_l]["fillStyle"];//取出渲染颜色
   var row=con_one[i_l]["row"];//取出当前行数
   var col=con_one[i_l]["col"];//取出当前列数
   con_one[i_l]["con"]=0;//重置为虚拟
   con_one[i_l]["fillStyle"]="pink";
   var k=lose_row.length-i_sc;//往下移动多少行;
   cube_obj[(row+k)*_this.settings.col+col]["fillStyle"]=fillStyle;//移动之后变色 
   cube_obj[(row+k)*_this.settings.col+col]["con"]=1;
   }
  }
  }
  if(clear_con){//绘制
  clearInterval(fall_1);//fall函数停止执行
  setTimeout(function(){//xxms之后重绘,给人消失的效果,为了做成这种效果,造成了fall函数和get_score函数的嵌套,应该有更好的思路
   ctx_s.clearRect(0,0,canvas_s.width,canvas_s.height);//提示屏清除
   cube_obj_s.forEach(function(e){//提示屏虚拟方块重新绘制
   e.draw(ctx_s);
   });
   ctx.clearRect(0,0,canvas.width,canvas.height);//清屏
   cube_obj.forEach(function(e){//重新绘制虚拟小方块
   e.draw(ctx);
   })
   shape_arr_t=_this.clone_obj(shape_arr_s);;//注“这里不能用var 声明变量,否则函数内部shape_arr的值会是undefined。
   shape_arr_t.re_point_x=(_this.settings.col/2)|0;//初始坐标做变化
   shape_arr_t.re_point_y=0;
   shape_arr_s=_this.draw_shape(ts,shape,shape_arr,shape_key,_this.make_color(color));
   shape_arr_s.draw(ctx_s);//重绘提示滑块
   if(score>=10&&score<20){//分数变化的话,则加速
   level_t=parseInt(k_t)+1;
   }else if(score>=20&&score<30){
   level_t=parseInt(k_t)+2;
   }else if(score>=30&&score<40){
   level_t=parseInt(k_t)+3;
   }else if(score>=40){
   level_t=parseInt(k_t)+4;
   }
   fall_1=setInterval(fall,_this.settings.v_dowm/level_t)//重新启动fall函数
  },250)
  }
  return clear_con;//用于判断后续作图
 };
 function fall(){//下落函数
  var arr_se=[];
  var con=true;
  var k=shape_arr_t["serial"].length;
//  for(var i=0;i<k;i++){//取出图形所有小块的纵坐标坐标
//  arr_se.push(shape_arr["serial"][i][1]);
//  }
//  var max_coor=Math.max.apply(null,arr_se);//取出最大的
  for(var i_que=0;i_que<k;i_que++){//判断所有小块下方的虚拟方块的con是否都是0
  var c_x_1=shape_arr_t["re_point_x"]+shape_arr_t["serial"][i_que][0];
  var c_y_1=shape_arr_t["re_point_y"]+shape_arr_t["serial"][i_que][1];
  //cube_obj[(c_y_1+1)*_this.settings.col+c_x_1]==undefined这种方法判断是否靠近底边
  if(cube_obj[(c_y_1+1)*_this.settings.col+c_x_1]==undefined||cube_obj[(c_y_1+1)*_this.settings.col+c_x_1].con==1){//到达底部或者下方任意滑块con==1的时候
   con=false;
   break;
  }
  }
  if(con){//不靠低边并且所有小块下方的虚拟方块的con都是0
  ctx.clearRect(0,0,canvas.width,canvas.height);//清屏
  shape_arr_t["re_point_y"]+=1; 
  cube_obj.forEach(function(e){//重新绘制虚拟小方块
   e.draw(ctx);
  })
  shape_arr_t.draw(ctx);//重绘滑块
  }else{
  end();
  for(var i_uni=0;i_uni<k;i_uni++){
   var c_x_2=shape_arr_t["re_point_x"]+shape_arr_t["serial"][i_uni][0];
   var c_y_2=shape_arr_t["re_point_y"]+shape_arr_t["serial"][i_uni][1];
   var c_color=shape_arr_t["serial"][i_uni][2];
   cube_obj[c_y_2*_this.settings.col+c_x_2].fillStyle=c_color;//修改对应虚拟方块颜色
   cube_obj[c_y_2*_this.settings.col+c_x_2].con=1;//修改对应虚拟方块con
  } 
  con_s=get_score();
  if(!con_s){//没有消除行的话
   ctx.clearRect(0,0,canvas.width,canvas.height);//清屏
   ctx_s.clearRect(0,0,canvas_s.width,canvas_s.height);//提示屏清除
   cube_obj_s.forEach(function(e){//提示屏虚拟方块重新绘制
   e.draw(ctx_s);
   });
   cube_obj.forEach(function(e){//重新绘制虚拟小方块
   e.draw(ctx);
   })
//   shape_arr=shape_arr_s;//注“这里不能用var 声明变量,否则函数内部shape_arr的值会是undefined。
//   shape_arr_s=_this.draw_shape(ts,shape,shape_arr,shape_key,_this.make_color(color));
//   shape_arr一个对象,因而是引用类型值,所以执行shape_arr=shape_arr_s的时候,只是做了指针的重新指向,shape_arr会随着shape_arr_s的改变而改变
   shape_arr_t=_this.clone_obj(shape_arr_s);
   shape_arr_t.re_point_x=(_this.settings.col/2)|0;//初始坐标做变化
   shape_arr_t.re_point_y=0;
   shape_arr_s=_this.draw_shape(ts,shape,shape_arr,shape_key,_this.make_color(color));
   shape_arr_s.draw(ctx_s);//重绘提示滑块
   shape_arr_t.draw(ctx);//重绘滑块
  }
  }
 };
 function end(){//判断游戏结束
  var con_end=false;
  for(var i=0;i<_this.settings.col;i++){
  if(cube_obj[i]["con"]==1){
   alert("到顶了,你好菜");
   con_end=true;
   break;
  }
  }
  if(con_end){
  location.reload();
  }
 }
 }
 tetris.prototype={//constructor将不在指向原函数,指向这个对象,如果需要,则写明
 extend:function(a,b){
  for(var key in a){
  if(a[key]!==undefined){
   b[key]=a[key];
  }
  }
  return b;
 },
 clone_obj:function(myObj){//克隆对象
   if(typeof(myObj) != 'object') return myObj; 
   if(myObj == null) return myObj; 
   if(myObj instanceof Array){//如果是数组的话,则创建新数组
    var myNewObj=new Array();
    for(var i=0;i<myObj.length;i++){
    myNewObj[i]=arguments.callee(myObj[i]);
    }
   }else{
    var myNewObj = new Object(); 
    for(var i in myObj) 
    myNewObj[i] = arguments.callee(myObj[i]);//递归。把所有的子对象都clone
   }
   return myNewObj; 
 },
 p_dom:function(a,b,c){//创建p标签
  var p=document.createElement("p");
  p.style.cssText="font-size: 20px;";
  p.className=c;
  p.innerText=b;
  a.appendChild(p);
 },
 input_dom:function(a,b){//创建input标签
  for(var i=1;i<=b;i++){
  if(i==1){
   var ch="checked";
  }else{
   ch="";
  }
  var label_d=document.createElement("label");
  var str='<input type="radio" name="level" value="'+i+'" '+ch+' class="level">级别'+i;
  label_d.innerHTML=str;
  a.appendChild(label_d);
  }
 },
 canvas_dom:function(a,b,c,d,e){//创建canvas标签
  var canvas=document.createElement("canvas");
  canvas.style.cssText="border:3px solid #333333;display: block;margin: 0 auto;";
  canvas.setAttribute("width",d*(b+e)-e);
  canvas.setAttribute("height",c*(b+e)-e);
  a.appendChild(canvas);
 },
 cube_obj_arr:function(a,b,c,d,e,f){//创建小方块集合,从左往右,从上往下,虚拟的,这里可以不画出来
  for(var i=0;i<c;i++){
  for(var j=0;j<d;j++){
   var cube_obj={
   x_start:(b+e)*j,
   y_start:(b+e)*i,
   col:j,//第几烈
   row:i,//第几行
   fillStyle:"pink",//默认颜色
   side_width:b,
   con:0,
   draw:function(a){
    a.beginPath();
    a.fillStyle=this.fillStyle;//this指向这个对象
    a.fillRect(this.x_start,this.y_start,this.side_width,this.side_width)
   }
   };//条件以后按需添加
   f.push(cube_obj);
  }
  }
  return f;
 },
 grid:function(a,b,c,d,e){//画网格
  for(var i=1;i<c;i++){//行数
  a.beginPath();
  a.moveTo(0,i*(b+e)-e);
  a.lineTo(d*(b+e)-e,i*(b+e)-e);
  a.lineWidth=e;
  a.stroke();
  }
  for(var i=1;i<d;i++){
  a.beginPath();
  a.moveTo(i*(b+e)-e,0);
  a.lineTo(i*(b+e)-e,c*(b+e)-e);
  a.lineWidth=e;
  a.stroke();
  }
 },
 make_color:function(a){//生成随机颜色
  var color_true="#";
  for(var i=0;i<6;i++){
  color_true+=a[Math.round(Math.random()*15)];
  }
  return color_true;
 },
 obj_list:function(a){//for...in 循环只遍历可枚举属性。像 Array 和 Object 使用内置构造函数所创建的对象都会继承自 Object.prototype 和 String.prototype 的不可枚举属性
  var key_arr=[];
  for(var key in a){
  key_arr.push(key);
  }
  return key_arr;
 },
 draw_shape:function(a,b,c,d,e){//生成下落滑块//ctx,shape,shape_arr,shape_key,_this.make_color(color)
  if(a=="ts"){
  c["re_point_x"]=0;
  c["re_point_y"]=0;
  }else{
  c["re_point_x"]=(this.settings.col/2)|0;//重置
  c["re_point_y"]=0;//重置
  }
  c["serial"].length=0;//重置
  var shape_list=d[Math.round(Math.random()*(d.length-1))];//对象中的任意值
  c["solo_key"]=shape_list;
  var b_length=b[shape_list].length;
  var mo_list=Math.round(Math.random()*(b_length-1));
  c["mo_list"]=mo_list;
  var s_cube=b[shape_list][mo_list];//任一一个图形集合,包裹l,r
  c["shape_solo"]=s_cube//s1-s7中的某一个中的某一个图形
  var s_mo=s_cube["mo"];//图形元素
  var k=s_mo.length;
  for(var i=0;i<k;i++){//这里的长度是4
  for(var j=0;j<k;j++){//这里的长度是4
   if(s_mo[i][j]){//如果等于1的话
   c["serial"].push([j,i,e])//需要绘制小块的坐标,所有小方块颜色一样,如果想变成不一样,则在for循环内部执行生成颜色函数,j,i才能对应图形
   }
  }
  }
  return c;
 }
 }
 window.tetris=tetris;
})()
</script>
 <script type="text/javascript">
 new tetris({
  dom:"box",
  side_length:25,
  row:20,
  col:12,
  v_dowm:500//设置级别1时的下落速度
 });
 </script>
</html>

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
CheckBox 如何实现全选?
Jun 23 Javascript
javascript实现面向对象类的功能书写技巧
Mar 07 Javascript
JS自动缩小超出大小的图片
Oct 12 Javascript
javascript中window.event事件用法详解
Dec 11 Javascript
jQuery如何将选中的对象转化为原始的DOM对象
Jun 09 Javascript
前端轻量级MVC框架CanJS详解
Sep 26 Javascript
2014年50个程序员最适用的免费JQuery插件
Dec 15 Javascript
JS上传图片前实现图片预览效果的方法
Mar 02 Javascript
深入理解JQuery循环绑定事件
Jun 02 Javascript
AngularJs Understanding the Model Component
Sep 02 Javascript
JS图片压缩(pc端和移动端都适用)
Jan 12 Javascript
vue结合axios与后端进行ajax交互的方法
Jul 06 Javascript
Vue + Webpack + Vue-loader学习教程之相关配置篇
Mar 14 #Javascript
canvas实现刮刮卡效果
Mar 14 #Javascript
Vue + Webpack + Vue-loader学习教程之功能介绍篇
Mar 14 #Javascript
jQuery插件HighCharts绘制的2D堆柱状图效果示例【附demo源码下载】
Mar 14 #Javascript
jQuery插件HighCharts实现的2D堆条状图效果示例【附demo源码下载】
Mar 14 #Javascript
前端框架学习总结之Angular、React与Vue的比较详解
Mar 14 #Javascript
jQuery ajax实现省市县三级联动
Mar 07 #Javascript
You might like
php&amp;mysql 日期操作小记
2012/02/27 PHP
微信公众平台开发之天气预报功能
2015/08/31 PHP
PHP 在数组中搜索给定的简单实例 array_search 函数
2016/06/13 PHP
PHP利用Socket获取网站的SSL证书与公钥
2017/06/18 PHP
xmlHTTP实例
2006/10/24 Javascript
JQuery插入DOM节点的方法
2015/06/11 Javascript
Javascript中setTimeOut和setInterval的定时器用法
2015/06/12 Javascript
jQuery实现鼠标划过添加和删除class的方法
2015/06/26 Javascript
JS不完全国际化&amp;本地化手册 之 理论篇
2016/09/27 Javascript
浅谈JavaScript正则表达式-非捕获性分组
2017/03/08 Javascript
利用node.js制作命令行工具方法教程(一)
2017/06/22 Javascript
jQuery 循环遍历改变a标签的href(实例讲解)
2017/07/12 jQuery
vue中的非父子间的通讯问题简单的实例代码
2017/07/19 Javascript
JavaScript实现计数器基础方法
2017/10/10 Javascript
p5.js入门教程之鼠标交互的示例
2018/03/16 Javascript
浅谈angularJs函数的使用方法(大小写转换,拷贝,扩充对象)
2018/10/08 Javascript
Nodejs中怎么实现函数的串行执行
2019/03/02 NodeJs
微信小程序开发(二):页面跳转并传参操作示例
2020/06/01 Javascript
javascript canvas实现简易时钟例子
2020/09/05 Javascript
[01:03:27]NAVI vs EG 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
[52:05]EG vs OG 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/18 DOTA
python函数缺省值与引用学习笔记分享
2013/02/10 Python
python基于xml parse实现解析cdatasection数据
2014/09/30 Python
Python 专题六 局部变量、全局变量global、导入模块变量
2017/03/20 Python
一个基于flask的web应用诞生 flask和mysql相连(4)
2017/04/11 Python
Python 读取图片文件为矩阵和保存矩阵为图片的方法
2018/04/27 Python
Django高并发负载均衡实现原理详解
2020/04/04 Python
解决python图像处理图像赋值后变为白色的问题
2020/06/04 Python
总经理职责
2013/12/22 职场文书
丧事主持词大全
2014/04/02 职场文书
本科毕业生应聘求职信
2014/07/06 职场文书
综治工作心得体会
2014/09/11 职场文书
安全生产工作汇报材料
2014/10/28 职场文书
2015年度培训工作总结范文
2015/04/02 职场文书
中学教师教学工作总结
2015/08/13 职场文书
素质教育培训心得体会
2016/01/19 职场文书