JavaScript 俄罗斯方块游戏实现方法与代码解释


Posted in Javascript onApril 08, 2020

本文实例讲述了JavaScript 俄罗斯方块游戏。分享给大家供大家参考,具体如下:

俄罗斯方块代码说明

/**
  名称:Javascript 俄罗斯方块!
  作者:Gloot
  邮箱:glootz@gmail.com 
   QQ:345268267 
  网站:http://www.cnblogs.com/editor/
*/

OLSFK = {};

本俄罗斯方块代码采用 JavaScript 脚本代码写成,简单易懂;

全代码采用静态类及静态变量成员组成;

全脚本通过实现代码全局配置 OLSFK.Options = {...}

定义方块起始坐标及定义各自的旋转点;

从初始化俄罗斯方块界面开始,再监听键盘事件;以及左右,向下及旋转动作判断,重新渲染方块位置;

判断是否消行,以及相应的加级判断,执行速度,加分操作来执行;

最后以判断是否当前级别大于所定义的最大级别来判断是否结束;

代码说明讲解

OLSFK.Options = { //相关参数
  width:12,//界面横向方块数
  height:20,//界面纵向方块数
  boxWidth : '16px',
  curLevel:1,
  speed : 1000, //setInterval,setTimeout
  direct : { //可以设定是A S D W, 还是← ↓ → 
    Down: 40 , /*run speed*/
    Left: 37,
    Right: 39,
    Rotate: 38
  },
  Move:true,//是否正在移动
  Eventing:false,
  Levels: {
    1:1000,
    2:900,
    3:800,
    4:700,
    5:600,
    6:500,
    7:400,
    8:300,
    9:200,
    10:100
  },
  curBlock:4, //当前移动的图形名称
  nextBlock:0,
  GampMap:new Object(),
  Timer:null,
  deline:0,
  Score:0,
  Deling:false,
  Start:false,
  lineNum:10, //删除几行了,加级
  ScoreNum:40 //消一行加分
}

direct 表示 使用键盘方位键来操作方块的移动方向;

使用哪种方向键按自由喜欢配置,比如字母键的A, S, D, W; 或右边小数字键盘的数字键各自的键盘编码;

比如 上(旋转)、下、左、右 方向键的编码分别为:38、40、37、39;

Levels:表示级别配置,本配置共分为10级,每个级别所对应的下落速度,即定时执行间隔;

curBlock:表示当前活动的方块;

nextBlock:表示接下来执行的方块索引,并显示界面右上角的预览框中;

GampMap:用于保存在根据定义行列数形成的游戏表格中保存每个格的数据信息;

OLSFK.Options.GampMap[x+'_'+y] = 0;

对象表格为: id: "box_"+x+"_"+y;

初始化数据为 ‘0';  表示该表格还未占用;当有占用时,设置值为 ‘1';

Timer:为定时执行器;setTimeout 定时执行方块下落的的频率;定时时间越小,速度越快;

Deling:当正在执行消行操作时,下次暂不显示并下落;

lineNum:表示消超过 10 行,加一级;

ScoreNum:表示每消一行所加的分数;

OLSFK.ReItems = function (cur){ //key旋转点
  
  switch (cur)
  {
    case 1:
      OLSFK.Items[1] = {//长块 LongBlock
                1:{x:4,y:0},
                2:{x:5,y:0},
                3:{x:6,y:0},
                4:{x:7,y:0},
                5:{x:5,y:0} //旋转点
              };
      break;
            //....
    }
}

该方法用于恢复方块的初始设置;

OLSFK.Next = { //key旋转点
  //长块 LongBlock
  1: {
    1:{x:0,y:1},
    2:{x:1,y:1},
    3:{x:2,y:1},
    4:{x:3,y:1}
  },
    //...
}

为不了不与游戏方块的设置冲突,独立出来下次随机方块的对象配置;

OLSFK.Items = { //key旋转点
  //长块 LongBlock
  1: {
    1:{x:4,y:0},
    2:{x:5,y:0},
    3:{x:6,y:0},
    4:{x:7,y:0},
    5:{x:5,y:0}
  },
  //方块Box
  2: {
    1:{x:4,y:0},
    2:{x:5,y:0},
    3:{x:4,y:1},
    4:{x:5,y:1},
    5:{x:0,y:0}
  },
  //凸块 TuBlock
  3: {
    1:{x:4,y:1},
    2:{x:5,y:0},
    3:{x:5,y:1},
    4:{x:6,y:1},
    5:{x:5,y:1}
  },
  //L块 LBlock
  4: {
    1:{x:5,y:0},
    2:{x:5,y:1},
    3:{x:5,y:2},
    4:{x:6,y:2},
    5:{x:5,y:2}
  },
  5: { //反向L块 FLBlock
    1:{x:5,y:2},
    2:{x:6,y:2},
    3:{x:6,y:1},
    4:{x:6,y:0},
    5:{x:6,y:2}
  },
  //Z块 ZBlock
  6: {
    1:{x:4,y:0},
    2:{x:5,y:0},
    3:{x:5,y:1},
    4:{x:6,y:1},
    5:{x:5,y:0}
  },
  7: {//反向Z块 FZBlock
    1:{x:4,y:1},
    2:{x:5,y:1},
    3:{x:5,y:0},
    4:{x:6,y:0},
    5:{x:5,y:1}
  }
}

方块共分为:长条块,方块,凸块(T块),L块,反L块,Z块,反Z块几种;

共7种方块,以1,2,3,4,5,6,7 索引键表示,方块是四个小块组成,每块都有各自的坐标,1-4表示组成该块的初始坐标位置,5表示旋转点;

OLSFK.Init = function() { //初始化界面
  //...
}

俄罗斯方块的界面初始化方法;将在 window.onload 中调用执行;

var w = OLSFK.Options.width;
  var h = OLSFK.Options.height;
  var total = w * h;

  var x=0,y=0;
  for (var i=0; i<total; i++)
  {
    
    OLSFK.Options.GampMap[x+'_'+y] = 0;

    Lib.Tag('SPAN',{id:"box_"+x+"_"+y,name:"cbox",style:{
      width:OLSFK.Options.boxWidth,
      height:OLSFK.Options.boxWidth,
      border:"2px outset #669",
      background:"#ddd",
      float:"left",
      overflow:"hidden"
    },innerHTML:" ",className:"cssbox"},back);

    var end = i + 1;
    x++;
    if (end >= w && end % w == 0)
    {
      x=0;
      y++;
      Lib.Tag('DIV',{style:{
        clear:"both"
      }},back);
    }

  }

通过设置的 Options.width, Options.height 列数与行数,以及设置的小方格宽度,初始化了一个宽:Options.width列,高为 Options.height 的表格界面出来;

Lib.Tag 用于创建标签对象;

Lib.Tag = function(TAG,json,pnode) {
  //...
}

TAG为标签名,比如: div, span 等;

json为设置标签样式 style;

pnode 是该创建所在的父容器;

OLSFK.Init = function() {} 还创建主游戏区域旁边的下次随机方块预览区,当前级别,及分数,以及操作“开始”,“暂停”按钮等;

JavaScript 俄罗斯方块游戏实现方法与代码解释

游戏初始入口点

window.onload = function() {
  if (window.isIE)
  {
    document.attachEvent("onkeydown",function(e) {
      if (OLSFK.Options.Start)
      {
        var E = OLSFK.KeyCode();
        OLSFK.EventFunc(E);
      }

    });

    document.attachEvent("onkeyup",function(e) {
      if (!OLSFK.Options.Move && OLSFK.Options.Start)
      {
        OLSFK.Options.Move = true;
        OLSFK.Options.Eventing = false;

        OLSFK.Options.Timer = setTimeout(function() {
          OLSFK.play();
        }, OLSFK.Options.Levels[OLSFK.Options.curLevel]);
      }
    });
  } else {
    document.addEventListener("keydown",function(e) {

      if (OLSFK.Options.Start)
      {
        var E = OLSFK.KeyCode();
        OLSFK.EventFunc(E);
      }
      
    },false);

    document.addEventListener("keyup",function(e) {
      if (!OLSFK.Options.Move && OLSFK.Options.Start)
      {
        OLSFK.Options.Move = true;
        OLSFK.Options.Eventing = false;

        OLSFK.Options.Timer = setTimeout(function() {
          OLSFK.play();
        }, OLSFK.Options.Levels[OLSFK.Options.curLevel]);
      }
    },false);
  }
  OLSFK.Init();
}

主要是监听键盘事件,根据 键盘事件 返回的按钮编码与 OLSFK.Options.direct 设置方向键匹配来操作方块的移动,旋转等;

keydown 用于操作下落方块的移动方向,旋转等;并重新绘制方块位置;

keyup 后继续按本级速度向下落;

OLSFK.Options.Levels[OLSFK.Options.curLevel]

表示当前级别对应的速度,即定时器间隔执行时间(毫秒);

OLSFK.EventFunc = function(code) {
  switch (code)
  {
    case OLSFK.Options.direct.Left: //LEFT
      if (!OLSFK.Options.Deling)
      {
        clearTimeout(OLSFK.Options.Timer);
        OLSFK.Options.Eventing = true;
        OLSFK.Options.Move = false;
        OLSFK.Left();
      }

      break;
      //...
    }
}

该方法是 监听 keydown 事件执行的动作;code 为按键 编码;

当判断未在消行动作时,清除定时器,OLSFK.Options.Eventing 设置为事件中 true,OLSFK.Options.Move 为 false 表示停止移动;

进入 向左移动方法 OLSFK.Left();

OLSFK.Left = function() {
  var block = OLSFK.Items[OLSFK.Options.curBlock];

  if (block)
  {
    var flag = true;
    for (var i=1; i<=4; i++)
    {
      var x = block[i].x;
      var y = block[i].y;

      if (x-1 < 0)
      {
        flag = false;
        break;
      }
      
      if (OLSFK.Options.GampMap[(x-1)+'_'+y] == 1 && !OLSFK.isMe(x-1,y))
      {
        flag = false;
        break;
      }
    }

    if (flag)
    {
      for (var i=1; i<=4; i++) //清除图形
      {
        var itm = block[i];
        var box = Lib.Getid('box_'+itm.x+'_'+itm.y);

        box.style.background = '#ddd';
        OLSFK.Options.GampMap[itm.x+'_'+itm.y] = 0;
      }

      for (var i=1; i<=5; i++)
      {
        var x = block[i].x;
        var y = block[i].y;

        OLSFK.Items[OLSFK.Options.curBlock][i] = {x:(x-1),y:y};
      }
      
      OLSFK.draw();
    } 
  }
}

var block = OLSFK.Items[OLSFK.Options.curBlock]; 表示获取当前移动方块;

if (OLSFK.Options.GampMap[(x-1)+'_'+y] == 1 && !OLSFK.isMe(x-1,y))
{
  flag = false;
  break;
}

判断该方块四个小方块左边是否有被占用的方块,也即: OLSFK.Options.GampMap[(x-1)+'_'+y] 为 1; 并且该位置块不属于方块自己的;

当左边方向无占用格时,清除当前方块四个小方块位置,重新绘制方块新坐标位置;并重置 相应的 OLSFK.Options.GameMap [x+y] 相应格的值;

当按钮 keyup 时,继承正常向下落;

OLSFK.isMe 代码:

OLSFK.isMe = function(x,y) {
  var block = OLSFK.Items[OLSFK.Options.curBlock];

  if (block)
  {
    for (var i=1; i<=4; i++)
    {
      if (block[i].x == x && block[i].y == y)
      {
        return true;
      }
    }
  }

  return false;
}

即指定的 x,y (参数值) 是否还在当前方块四个坐标内;

OLSFK.Right () 与 Left() 一样;

旋转方块代码;

OLSFK.Rotate = function() {
  var block = OLSFK.Items[OLSFK.Options.curBlock];

  if (block)
  {
    var flag = true;

    var R = block[5];
    for (var i=1; i<=4; i++)
    {
      var x = block[i].x;
      var y = block[i].y;

      if (R.x == x && R.y == y)
      {
        
      } else {
        var nson = new Object();

        nson.x = R.x + R.y - y;
        nson.y = R.y - R.x + x;

        if ( nson.x < 0 || nson.y < 0 || nson.x >= OLSFK.Options.width || nson.y >= OLSFK.Options.height )
        {
          flag = false;
          break;
        }

        if (OLSFK.Options.GampMap[nson.x+'_'+nson.y] == 1 && !OLSFK.isMe(nson.x,nson.y))
        {
          flag = false;
          break;
        }
      }
    }

    if (flag)
    {
      for (var i=1; i<=4; i++) //清除图形
      {
        var itm = block[i];
        var box = Lib.Getid('box_'+itm.x+'_'+itm.y);

        box.style.background = '#ddd';
        OLSFK.Options.GampMap[itm.x+'_'+itm.y] = 0;
      }

      var Pnt = 1;

      for (var i=1; i<=4; i++)
      {
        var x = block[i].x;
        var y = block[i].y;
        
        if (R.x == x && R.y == y)
        {
          Pnt = i;
        } else {
          var nson = new Object();

          nson.x = R.x + R.y - y;
          nson.y = R.y - R.x + x;
          OLSFK.Items[OLSFK.Options.curBlock][i] = {x:nson.x,y:nson.y};
        }
      }

      OLSFK.Items[OLSFK.Options.curBlock][5] = OLSFK.Items[OLSFK.Options.curBlock][Pnt];
      
      OLSFK.draw();
    } 
  }
}

var R = block[5]; 就是获取旋转点;

就开始对方块四个小块以旋转点为中心,逆时针旋转(并不全是 90 度);当当前块不为旋转点时,旋转公式;

var nson = new Object();

nson.x = R.x + R.y - y;
nson.y = R.y - R.x + x;

这个公式要这样看;

ResultX = RotateX + (RotateY - CurrentY);
ResultY = RotateY - (RotateX - CurrentX);

//Y的偏移量,就是X的增加值;
//反之同

当旋转四周都无占用物时;清除当前图形,重绘旋转后的图形位置;

重置 OLSFK.Options.GampMap[itm.x+'_'+itm.y] 各个方块的占用值;

OLSFK.Random = function() {

  if (OLSFK.Options.nextBlock != 0)
  {
    OLSFK.Options.curBlock = OLSFK.Options.nextBlock;

    var block = OLSFK.Next[OLSFK.Options.nextBlock];
    if (block)
    {
      for (var i=1; i<=4; i++)
      {
        var itm = block[i];
        var box = Lib.Getid('cur_'+itm.x+'_'+itm.y);

        box.style.background = '#ddd';
        //OLSFK.Options.GampMap[itm.x+'_'+itm.y] = 0;
      }
    }
  } else {
    OLSFK.Options.curBlock = Math.floor(Math.random() * 7 + 1);
  }
  OLSFK.Options.nextBlock = Math.floor(Math.random() * 7 + 1);

  OLSFK.drawNext();
}

随机生成下次预下落的方块;并显示到右上角的预览表格里;

OLSFK.play = function(speed) {
  var block = OLSFK.Items[OLSFK.Options.curBlock];

  if (block && OLSFK.Options.Move)
  {

    var flag = true;
    for (var i=1; i<=4; i++)
    {
      var x = block[i].x;
      var y = block[i].y;

      if (y+1 >= OLSFK.Options.height)
      {
        flag = false;
        break;
      }
      
      if (OLSFK.Options.GampMap[x+'_'+(y+1)] == 1 && !OLSFK.isMe(x,y+1))
      {
        flag = false;
        break;
      }
    }
    
    if (flag)
    {
      for (var i=1; i<=4; i++) //清除图形
      {
        var itm = block[i];
        var box = Lib.Getid('box_'+itm.x+'_'+itm.y);

        box.style.background = '#ddd';
        OLSFK.Options.GampMap[itm.x+'_'+itm.y] = 0;
      }

      for (var i=1; i<=5; i++)
      {
        var x = block[i].x;
        var y = block[i].y;

        OLSFK.Items[OLSFK.Options.curBlock][i] = {x:x,y:(y+1)};
      }
      OLSFK.draw();
      
      var S = OLSFK.Options.Levels[OLSFK.Options.curLevel];
      if (speed)
      {
        S = 50;
      }
      OLSFK.Options.Timer = setTimeout(function() {
        OLSFK.play();
      }, S);
    } else {
      OLSFK.ReItems(OLSFK.Options.curBlock);
      OLSFK.newRun();
    }
  }
}

OLSFK.play 正常下落的方法,也得判断下落一格是否有被占用的格,如果没有,清除当前方块,绘制方块新位置;

当方块不能再下落时(flag = false),重置当前方块坐标配置; OLSFK.ReItems(OLSFK.Options.curBlock);

进入 OLSFK.newRun(); 新下落方块下落过程准备;

OLSFK.newRun = function() {

  clearTimeout(OLSFK.Options.Timer);
  OLSFK.DelFunc();
  if (OLSFK.Options.deline >= 10)
  {
    OLSFK.Options.deline = 0;
    OLSFK.Options.curLevel ++;
    OLSFK.Element.CurLevel.setHTML("级:"+OLSFK.Options.curLevel);
  }
  OLSFK.Element.Score.setHTML("分:"+OLSFK.Options.Score);

  if (OLSFK.Options.curLevel <= OLSFK.Options.lineNum)
  {
    OLSFK.Random();
    //判断是否结束
    OLSFK.ChkEnd();
  } else {
    OLSFK.Options.Move = false;
    OLSFK.Options.Start = false;
    OLSFK.Options.Eventing = false;
    OLSFK.Options.Deling = false;
    Lib.Getid('spn').innerHTML = 'Game Is Over! You Win the Game!';

    Lib.Getid('dobtn').innerHTML = ' 开始 ';
  }
  
}

当下落结束时,清除定时器,暂停新方块下落,检测是否有可消除的行;减了多少行;

每减去一行 加分

OLSFK.Options.Score += OLSFK.Options.ScoreNum;

这个方法在 :

OLSFK.DelFunc = function() {
  OLSFK.Options.Deling = true;
  OLSFK.Options.Move = false;
  OLSFK.Options.Eventing = false;
  var Fn = 0;
  for (var i=OLSFK.Options.height-1; i>=0; i--)
  {
    Fn = 0;
    for (var j=0; j<OLSFK.Options.width; j++)
    {
      if (OLSFK.Options.GampMap[j+'_'+i] == 1)
      {
        Fn++;
      }
    }

    if (Fn == OLSFK.Options.width)
    {
      OLSFK.Options.deline ++;
      OLSFK.Options.Score += OLSFK.Options.ScoreNum;
      OLSFK.DelLine(i);
      i++;
    }
  }
  OLSFK.Options.Deling = false;
  OLSFK.Options.Move = true;
  OLSFK.Options.Eventing = true;
}

中执行;

减完一行,就重置该行以上所有行往下降一行;并重置 :

OLSFK.Options.GampMap[x+'_'+y] = OLSFK.Options.GampMap[x+'_'+(y-1)];

该减行为上行的数据;

if (Fn == OLSFK.Options.width)
    {
      OLSFK.Options.deline ++;
      OLSFK.Options.Score += OLSFK.Options.ScoreNum;
      OLSFK.DelLine(i);
      i++;
    }

该判断表示该行上所有格都被占用到;

回到 newRun 上,当判断消行超过几行时,即加级;

if (OLSFK.Options.curLevel <= OLSFK.Options.lineNum)
  {
    OLSFK.Random();
    //判断是否结束
    OLSFK.ChkEnd();
  }

如果级数小于配置的总级数,则进入 OLSFK.random();

设置当前下落方块,并随机生成下次下落方块并预览右上角表格上;

OLSFK.ChkEnd = function() {
  var block = OLSFK.Items[OLSFK.Options.curBlock];

  if (block && OLSFK.Options.Move)
  {

    var flag = true;
    for (var i=1; i<=4; i++)
    {
      var x = block[i].x;
      var y = block[i].y;
      
      if (OLSFK.Options.GampMap[x+'_'+y] == 1)
      {
        flag = false;
        break;
      }
    }
  }

  if (flag )
  {
    OLSFK.draw();

    //定时往下掉
    OLSFK.Options.Timer = setTimeout(function() {
      OLSFK.play();
    }, OLSFK.Options.Levels[OLSFK.Options.curLevel]);
  } else {
    OLSFK.Options.Move = false;
    OLSFK.Options.Start = false;
    OLSFK.Options.Eventing = false;
    OLSFK.Options.Deling = false;
    Lib.Getid('spn').innerHTML = 'Game Is Over';

    Lib.Getid('dobtn').innerHTML = ' 开始 ';
  }
}

当当前下落的方块进入表格上有被占用的格子,即被卡住,游戏结束;

反之 则 setTimeout 开始新方块的下落动作;

其他方法说明

OLSFK.Event = function() {
  if (window.isIE)
    return window.event;
  func = OLSFK.Event.caller; 

  while(func!=null) 
  {
    var arg0=func.arguments[0]; 
    if(arg0) 
    {
      return arg0; 
    }
    func=func.caller; 
  }
  return null; 
}

OLSFK.KeyCode = function() {
  return OLSFK.Event().keyCode || OLSFK.Event().which;
}

OLSFK.Event = function();

这是一种获取当前事件的方法,可以比较兼容获取当前的事件;

俄罗斯方块 JavaScript 代码

点击此处本站下载

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
js清理Word格式示例代码
Feb 13 Javascript
js中settimeout方法加参数
Feb 28 Javascript
JavaScript定义类和对象的方法
Nov 26 Javascript
js鼠标滑过图片震动特效的方法
Feb 17 Javascript
使用HTML+CSS+JS制作简单的网页菜单界面
Jul 27 Javascript
原生javascript实现分享到朋友圈功能 支持ios和android
May 11 Javascript
JQ选择器_选择同类元素的第N个子元素的实现方法
Sep 08 Javascript
jquery实现(textarea)placeholder自动换行
Dec 22 Javascript
js获取指定时间的前几秒
Apr 05 Javascript
jQuery初级教程之网站品牌列表效果
Aug 02 jQuery
Node.js 使用request模块下载文件的实例
Sep 05 Javascript
Angular Excel 导入与导出的实现代码
Apr 17 Javascript
vue与iframe之间的信息交互的实现
Apr 08 #Javascript
Javascript摸拟自由落体与上抛运动原理与实现方法详解
Apr 08 #Javascript
antd-mobile ListView长列表的数据更新遇到的坑
Apr 08 #Javascript
详解element上传组件before-remove钩子问题解决
Apr 08 #Javascript
javascript 设计模式之享元模式原理与应用详解
Apr 08 #Javascript
javascript 设计模式之组合模式原理与应用详解
Apr 08 #Javascript
JS async 函数的含义和用法实例总结
Apr 08 #Javascript
You might like
PHP3 safe_mode 失效漏洞
2006/10/09 PHP
PHP+FLASH实现上传文件进度条相关文件 下载
2007/07/21 PHP
两个强悍的php 图像处理类1
2009/06/15 PHP
php Smarty初体验二 获取配置信息
2011/08/08 PHP
PHP笔记之:基于面向对象设计的详解
2013/05/14 PHP
php ci框架中加载css和js文件失败的解决方法
2014/03/03 PHP
新浪SAE搭建PHP项目教程
2015/01/28 PHP
PHP查找数值数组中不重复最大和最小的10个数的方法
2015/04/20 PHP
php的无刷新操作实现方法分析
2020/02/28 PHP
prototype 1.5相关知识及他人笔记
2006/12/16 Javascript
几个javascript操作word的参考代码
2009/10/26 Javascript
JavaScript中数组成员的添加、删除介绍
2014/12/30 Javascript
javascript组合使用构造函数模式和原型模式实例
2015/06/04 Javascript
详解AngularJS的通信机制
2015/06/18 Javascript
html+js实现简单的计算器代码(加减乘除)
2016/07/12 Javascript
JavaScript鼠标特效大全
2016/09/13 Javascript
JavaScript中this的四个绑定规则总结
2016/09/26 Javascript
vue2.0结合DataTable插件实现表格动态刷新的方法详解
2017/03/17 Javascript
node文件上传功能简易实现代码
2017/06/16 Javascript
webstorm中vue语法的支持详解
2018/05/09 Javascript
js实现web调用摄像头 js截取视频画面
2019/04/21 Javascript
JavaScript基于SVG的图片切换效果实例代码
2020/12/15 Javascript
python使用pil进行图像处理(等比例压缩、裁剪)实例代码
2017/12/11 Python
[原创]Python入门教程5. 字典基本操作【定义、运算、常用函数】
2018/11/01 Python
python实现对指定字符串补足固定长度倍数截断输出的方法
2018/11/15 Python
Python 正则表达式 re.match/re.search/re.sub的使用解析
2019/07/22 Python
jupyter notebook 添加kernel permission denied的操作
2020/04/21 Python
matplotlib 画双轴子图无法显示x轴的解决方法
2020/07/27 Python
Python配置pip国内镜像源的实现
2020/08/20 Python
详解Html5 监听拦截Android返回键方法
2018/04/18 HTML / CSS
html5写一个BUI折叠菜单插件的实现方法
2019/09/11 HTML / CSS
运动会铅球比赛加油稿
2014/09/26 职场文书
新教师个人工作总结
2015/02/06 职场文书
运动会运动员赞词
2015/07/22 职场文书
导游词之淮安明祖陵
2019/11/25 职场文书
Oracle 多表查询基本语法实例
2022/04/18 Oracle