一个简单的九宫格排序游戏js实现
游戏介绍
开心或者不开心的时候,写点简单的小东西也是个不错的选择。
这次写的是一个十分简单的九宫格排序游戏,样子是这样的:
通关的条件很简单:把数字按照从小到大的顺序排列好就可以了。也就是排列成这样:
下面来说一下实现:
实现思路
HTML部分
分层次的说,页面主要分两块:左边是一块名为 game 的 div 区域,用于放置游戏需要的棋盘和小方块;右边一块 div 名为 control,用来显示游戏时间和按钮:
上图基本展示了代码的层级关系,具体代码如下所示:
<html>
<head>
<title>九宫格排序游戏</title>
<link rel="stylesheet" href="jiugongge.css">
</head>
<body>
<div id="all">
<!--总体区域-->
<div id="game">
<!--九宫格区域-->
<div id="d1" onclick="move(1)">1</div>
<div id="d2" onclick="move(2)">2</div>
<div id="d3" onclick="move(3)">3</div>
<div id="d4" onclick="move(4)">4</div>
<div id="d5" onclick="move(5)">5</div>
<div id="d6" onclick="move(6)">6</div>
<div id="d7" onclick="move(7)">7</div>
<div id="d8" onclick="move(8)">8</div>
</div>
<div id="control">
<!--控制按钮区域-->
<span id="timeText">总用时</span>
<span id="timer"></span>
<button id="start" onclick="start()">开始</button>
<button id="restart" onclick="restart()">重新开始</button>
</div>
</div>
<script src="jiugongge.js"></script>
</body>
</html>
CSS 部分
CSS部分是最吼的!
为了避免浏览器不同标准导致的意外,我先无脑拷贝一份CSS初始化代码(来自淘宝网)。
body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button, input, textarea, th, td { margin:0; padding:0; }
body, button, input, select, textarea { font:12px/1.5tahoma, arial, \5b8b\4f53; }
h1, h2, h3, h4, h5, h6{ font-size:100%; }
address, cite, dfn, em, var { font-style:normal; }
code, kbd, pre, samp { font-family:couriernew, courier, monospace; } small{ font-size:12px; }
ul, ol { list-style:none; }
a { text-decoration:none; }
a:hover { text-decoration:underline; }
sup { vertical-align:text-top; }
sub{ vertical-align:text-bottom; }
legend { color:#000; }
fieldset, img { border:0; }
button, input, select, textarea { font-size:100%; }
table { border-collapse:collapse; border-spacing:0; }
/* 样式初始化*/
接下来我把想说的直接写在注释里,值得一提的是,小方块(棋子)滑动的实现用的是 CSS 的transition
实现的,比 JS 实现方便到不知道哪里去了(大雾(/ω\))。
完整的CSS代码:
body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button, input, textarea, th, td { margin:0; padding:0; }
body, button, input, select, textarea { font:12px/1.5tahoma, arial, \5b8b\4f53; }
h1, h2, h3, h4, h5, h6{ font-size:100%; }
address, cite, dfn, em, var { font-style:normal; }
code, kbd, pre, samp { font-family:couriernew, courier, monospace; } small{ font-size:12px; }
ul, ol { list-style:none; }
a { text-decoration:none; }
a:hover { text-decoration:underline; }
sup { vertical-align:text-top; }
sub{ vertical-align:text-bottom; }
legend { color:#000; }
fieldset, img { border:0; }
button, input, select, textarea { font-size:100%; }
table { border-collapse:collapse; border-spacing:0; }
/* 样式初始化*/
body { /*浏览器屏幕大小自动适配 */
width: 100%;
height: 100%;
}
#all {
position: relative;
width: 100%;
height: 800px;
margin: 0 auto; /*居中*/
margin-top: 20px; /*我开心才写的,你不喜欢可以删掉*/
}
#game { /*大div(棋盘)*/
position: absolute;
display: inline-block;
width: 450px;
height:450px;
background-color: #66ffcc; /*初音绿*/
box-shadow: 0 0 10px #66ffcc;
margin: 0 auto; /*居中*/
top:0px;
right: 0px;
bottom: 0px;
left: 0px;
}
#game div { /*小div(棋子)*/
position: absolute;
width: 149px;
height: 149px;
box-shadow: 1px 1px 2px #777;
background-color: #66ccff; /*天依蓝*/
color: white;
text-align: center;
font-size: 150px;
line-height: 150px;
cursor: pointer;
-webkit-transition: 0.3s;/*浏览器前缀,兼容其他浏览器 safari && chrome*/
-moz-transition: 0.3s;/*firefox*/
-ms-transition: 0.3s;/*ie*/
-o-transition: 0.3s;/*opera*/
transition: 0.3s;
}
#game div:hover{
color: #ffe171; /*悬浮变色*/
}
#control {
width: 150px;
height: 450px;
display: inline-block;
float: right;
position: absolute;
left:80%;
}
#control button,span{
height: 25px;
font-size: 20px;
color: #222;
margin-top: 10px;
}
#start{
display: inline-block;
width: 100px;
background-color: #66ccff;
text-shadow: 1px 1px 2px #ffe171;/*字体阴影*/
border-radius: 5px; /*圆角属性*/
box-shadow: 2px 2px 5px #4c98f5;/*盒子阴影*/
text-align: center; /*文字居中*/
cursor: pointer; /*光标变形*/
}
#restart{
display: inline-block;
width: 100px;
background-color: #66ccff;
text-shadow: 1px 1px 2px #ffe171;
border-radius: 5px;
box-shadow: 2px 2px 5px #4c98f5;
text-align: center;
cursor: pointer;
}
/*一一确定小div(棋子)的初始位置,本游戏棋盘宽450px*/
#d1{
left: 0px;
}
#d2{
left: 150px;
}
#d3{
left: 300px;
}
#d4{
top: 150px;
}
#d5{
top: 150px;
left: 150px;
}
#d6{
top: 150px;
left: 300px;
}
#d7{
top: 300px;
}
#d8{
left: 150px;
top: 300px;
}
JS 部分
JS代码中,主要有这么几个函数:
move(id)
传入值是当前被点击的小方块(棋子)的编号(该编号只和棋子本身的数字有关,和棋盘上的位置无关),当小方块(棋子)被点击时,会调用whereCanTo(cur_div)
函数来判断是否能移动和能移动到那个位置,并进行移动。whereCanTo(cur_div)
传入值是当前被点击的小方块在棋盘上的位置(和棋子本身的编号无关),返回值是一个数字,表示能移动到的位置,返回0表示不能移动。start()
点击『开始/暂停』按钮后触发,该按钮对应的方法是一个toggle,暂停时调用会开始游戏,进行游戏的时候调用会暂停游戏。该方法会通过setInterval(timer,1000)
调用timer()
方法,以此实现计时器。
*timer()
start()
方法每1000毫秒调用一次,以此实现计时器,并把时间显示到 DOM 中。
restart()
点击『重新开始』时调用该方法,会把时间计0,然后调用random_d()
方法把棋子全部打乱。random_d()
将所有的棋子打乱,实现的思路是模拟人的操作。
全部代码如下:
var time = 0;
var pause = true; //值为 true 的时候,说明当前状态为暂停
var set_timer;
var d = Array(10); //用于保存小div的编号
var d_direct = new Array( //记录当前位置的方块可以移动到哪些位置
[0], //为了逻辑简明,不使用第一项
[2,4], //1号位的方块可以移动到2号位和4号位
[1,3,5],
[2,6],
[1,5,7],
[2,4,6,8],
[3,5,9],
[4,8],
[5,7,9],
[6,8]
);
var d_position = new Array( //保存方块的位置
[0], //为了逻辑简明,不使用第一项
[0,0], //第一个表示left,第二个表示top,比如第一块的位置为let:0px,top:0px
[150,0], //方块的大小为 150px * 150px
[300,0],
[0,150],
[150,150],
[300,150],
[0,300],
[150,300],
[300,300]
);
d[1]=1;d[2]=2;d[3]=3;d[4]=4;d[5]=5;d[6]=6;d[7]=7;d[8]=8;d[9]=0; //默认按照顺序排好,方块只有八块,第九块没有,
//所以为0,我们用0表示空白块
function move(id) { //用于移动方块的函数
var i = 1;
while(d[i] != id){ // 用于确定当前触发函数的是哪个区域(编号属于大DIV而不是小方块)
i++;
}
var target_d = 0; //用于保存小方块可以去的区域,0表示不能移动
target_d = whereCanTo(i);
//用来找出小DIV可以去的位置,如果返回0,表示不能移动,如果可以移动,则返回可以去的位置编号
if(target_d != 0){
d[i] = 0; //如果当前可以移动,则把当前小方块移走,当前位置设置为无方块
d[target_d] = id; //把目标大DIV设置为被点击的小DIV的编号
document.getElementById("d"+id).style.left = d_position[target_d][0]+"px";
document.getElementById("d"+id).style.top = d_position[target_d][1]+"px";
}
var finish_flag = true;
for(var k=1; k<9; ++k){ //用于判断游戏是否通关
if( d[k] != k){
finish_flag=false;
break;
//如果大DIV保存的编号和它本身的编号不同,则表示还不是全部按照顺序排的,那么设置为false,跳出循环
}
}
if(finish_flag==true){
if(!pause)
start(); //此时调用 start() 函数,效果是暂停游戏,
//start() 是一个 toggle,暂停时调用会开始游戏,进行游戏的时候调用会暂停游戏
alert("恭喜通关!");
}
}
function whereCanTo(cur_div) { //参数是大div的编号
var j = 0;
var move_flag = false; //方块能否移动的标记
for(j=0; j<d_direct[cur_div].length; j++){
if (d[ d_direct[cur_div][j] ]== 0) {
move_flag = true;
break;
}
}
if(move_flag){
return d_direct[cur_div][j];
}
else{
return 0;
}
}
//定时函数,每一秒执行一次
function timer(){
time+=1;//一秒钟加一,单位是秒
var min=parseInt(time/60);//把秒转换为分钟
var sec=time%60;//取余就是秒
document.getElementById("timer").innerHTML=min+"分"+sec+"秒";//把时间更新显示出来
}
function start(){
if(pause){
document.getElementById("start").innerHTML = "暂停"; //当前状态为游戏进行中,把开始按钮切换为暂停
pause = false;//暂停标志设置为false
set_timer = setInterval(timer,1000);//启动定时,每1000毫秒调用一次
}else{
document.getElementById("start").innerHTML = "开始";
pause = true;
clearInterval(set_timer);
}
}
function restart(){
time=0;//把时间设置为0
random_d();//把方块随机打乱函数
if(pause)//如果暂停,则开始计时
start();
}
function random_d(){ //模拟人的操作,为了避免随机的时候直接碰巧通关,所以先走上几步
move(6);
move(5);
move(2);
move(1);
move(4);
var i = 30; //要是觉得打散的不够彻底,可以多循环几次。
while(i>0){
move(parseInt(Math.random()*9));
i--;
}
}
window.onload=function(){ //初始化函数,页面加载的时候调用重置函数,重新开始
restart();
}
实际上,还可以使用另外一种打散的方式,原理是随机调换小div的位置,但是这样做会有相当的概率导致你永远都不能通关,随机打散的代码如下:
function random_d(){
for(var i=9; i>1; --i){
var to=parseInt(Math.random()*(i-1)+1);//产生随机数,范围为1到i,不能超出范围,因为没这个id的DIV
if(d[i]!=0){ //把当前的DIV位置设置为随机产生的DIV的位置
document.getElementById("d"+d[i]).style.left=d_position[to][0]+"px";
document.getElementById("d"+d[i]).style.top=d_position[to][1]+"px";
}
if(d[to]!=0){ //把随机产生的DIV的位置设置为当前的DIV的位置
document.getElementById("d"+d[to]).style.left=d_position[i][0]+"px";
document.getElementById("d"+d[to]).style.top=d_position[i][1]+"px";
}
var tem=d[to]; //然后把它们两个的DIV保存的编号对调一下
d[to]=d[i];
d[i]=tem;
}
}
代码部分:
所有的代码都在文章中写出来了,github的地址点此。
Comments