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 相关文章推荐
jquery动画1.加载指示器
Aug 24 Javascript
jQuery中filter()和find()的区别深入了解
Sep 25 Javascript
jQuery源码分析之jQuery.fn.each与jQuery.each用法
Jan 23 Javascript
深入了解JavaScript中的Symbol的使用方法
Jul 28 Javascript
Extjs 点击复选框在表格中增加相关信息行
Jul 12 Javascript
基于AngularJS实现iOS8自带的计算器
Sep 12 Javascript
javascript中活灵活现的Array对象详解
Nov 30 Javascript
jQuery日期范围选择器附源码下载
May 23 jQuery
JS表单提交验证、input(type=number) 去三角 刷新验证码
Jun 21 Javascript
js中json对象和字符串的理解及相互转化操作实现方法
Sep 22 Javascript
react native 获取地理位置的方法示例
Aug 28 Javascript
浅谈react路由传参的几种方式
Mar 23 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
最令PHP初学者们头痛的十四个问题
2007/01/15 PHP
深入理解PHP 数组之count 函数
2016/06/13 PHP
PHP递归获取目录内所有文件的实现方法
2016/11/01 PHP
PHP高并发和大流量解决方案整理
2019/12/24 PHP
laravel通用化的CURD的实现
2019/12/13 PHP
jQuery源码分析-03构造jQuery对象-工具函数
2011/11/14 Javascript
让人印象深刻的10个jQuery手风琴效果应用
2012/05/08 Javascript
jQuery页面图片伴随滚动条逐渐显示的小例子
2013/03/21 Javascript
js切换光标示例代码
2013/10/10 Javascript
javascript包装对象实例分析
2015/03/27 Javascript
使用Browserify配合jQuery进行编程的超级指南
2015/07/28 Javascript
浅析Node.js的Stream模块中的Readable对象
2015/07/29 Javascript
AngularJS实现分页显示数据库信息
2016/07/01 Javascript
AngularJS基础 ng-non-bindable 指令详细介绍
2016/08/02 Javascript
jQuery Easyui DataGrid点击某个单元格即进入编辑状态焦点移开后保存数据
2016/08/15 Javascript
bootstrap配合Masonry插件实现瀑布式布局
2017/01/18 Javascript
微信小程序 弹框和模态框实现代码
2017/03/10 Javascript
js编写简单的聊天室功能
2017/08/17 Javascript
Router解决跨模块下的页面跳转示例
2018/01/11 Javascript
微信小程序实现导航栏选项卡效果
2020/06/19 Javascript
JavaScript之实现一个简单的Vue示例
2019/01/17 Javascript
vue用elementui写form表单时,在label里添加空格操作
2020/08/13 Javascript
[01:04:02]DOTA2-DPC中国联赛 正赛 Elephant vs IG BO3 第二场 1月24日
2021/03/11 DOTA
python使用pymysql实现操作mysql
2016/09/13 Python
python http基本验证方法
2018/12/26 Python
python 提取tuple类型值中json格式的key值方法
2018/12/31 Python
python模块导入的方法
2019/10/24 Python
python 实现dict转json并保存文件
2019/12/05 Python
Python使用configparser库读取配置文件
2020/02/22 Python
Python单例模式的四种创建方式实例解析
2020/03/04 Python
浅析关于Keras的安装(pycharm)和初步理解
2020/10/23 Python
css3 实现滚动条美化效果的实例代码
2021/01/06 HTML / CSS
餐饮业会计岗位职责
2013/12/19 职场文书
年度评优评先方案
2014/06/03 职场文书
校园环保标语
2014/06/13 职场文书
mysql使用instr达到in(字符串)的效果
2022/04/03 MySQL