金山打字小游戏
1.介绍及效果图
这是一个练习打字的游戏,当游戏开始后,界面从顶部不断落下内容为随机字母的方块,当按下相对应的按键时,就会清除对应方块
2.思路
1、如何确定难度?
2、方块如何生成?生成位置?如何移动?
3、如何键盘检测?对应后如何移除?
4、移除之后分数的累加?
5、游戏失败?弹窗,结算分数?
3.代码
1、body部分:
<div id="box">
<div id="changeBox">
<ul class="change">
<p>金山打字</p> //标题
<li>初级</li> //难度选项
<li>中级</li>
<li>高级</li>
</ul>
</div>
</div>
2、css部分:
<style type="text/css">
//清除默认margin、padding值
*{margin: 0;padding: 0;box-sizing: border-box;}
//大盒子样式设置
#box{width: 500px;height: 800px;background: black;position: relative;/* overflow: hidden; */padding-top: 200px;}
//方块样式设置
.block{width: 30px;height: 30px;background: blue;position: absolute;top: 0px;text-align: center;
line-height: 30px;color:white;font-weight: bold;}
//标题框样式设置
#changeBox{width: 300px;height: 200px;background: #ADFF2F;margin: 0 auto;}
选项样式设置
li{list-style: none;height: 50px;text-align: center;line-height: 50px;border-top: 1px solid red;cursor: pointer;}
//标题样式设置
p{line-height: 50px;text-align: center;background: #666666;font-size: 18px;font-weight: bold;color: white;}
</style>
3、js部分详解:
****1. 初始标题框元素获取、选项卡索引获取以及实例的创建:****
这里要获取选项卡的li以及其所在的盒子
var ul = document.querySelectorAll('li'); //获取所有li,存放在ul这个集合中
var changBox = document.getElementById('changeBox');
****2. 用for循环遍历li,并取得对应li的下标****
for(var i=0;i<ul.length;i++){
ul[i].x = i; //给每一个li定义一个自定义属性x,属性值为i;此时,属性x的值与其索引值是相等的
ul[i].onclick=function(){ //当点击li的时候
var index = this.x; //新建一个变量,让他等于当前点击的li的属性x的值;
changeBox.style.display = 'none'; //选择之后,隐藏标题框
new game({ind:index}); //新建一个实例game,并且将当前li的索引值以对象方式进行传参
}
}
!!!核心:给li定义属性,通过this指向当前被点击的li,并取到索引值
****3.实例game初始的设置****
function game(p){ //用p来接受传进来的实参,也就是被点击的li的索引值
this.box = document.getElementById('box');
this.plane = document.querySelector('.plane');
this.block = document.querySelector('.block');
this.t; //定时器,后面在方块生成中会用到
this.text=0; //记录分数
this.blockArr = [];//存放生成的方块
this.number=p.ind;// 拿到索引,确定难度
switch(this.number){
case 0:this.number = 500;break;
case 1:this.number = 300;break;
case 2:this.number = 100;break;
} //这里的更新的this.number的值代表了后面定时器的事件,毫秒级
this.blockCreat() //调用生成方块的函数
}
这里没有用var来声明变量,而是用了this,是为了后面作用域会用到
****4.方块的生成,这里用到了定时器,每隔多少秒就会有一个方块生成,从上方落下****
game.prototype.blockCreat = function(){
that = this; 新建变量that=this,因为this跟着函数走,在定时器里有写了一个函数,防止拿不到外面的this
clearInterval(this.t) //每次执行都要先清除定时器,不然会越来越快,速度会叠加
this.t = setInterval(function(){ //开启定时器
var count = random(0,that.box.offsetWidth-30);//方块初始水平位置,random为封装好的函数,根据传的参数,
会取到 0 ~(盒子宽度-方块宽度),【封装的函数在后面】
var val; //val是用来存随机生成的数字转换后的字母
that.oblock = document.createElement('div'); //创建方块(div)
that.oblock.className = 'block'; //设置方块的class名
that.oblock.style.left = count + 'px'; //方块左偏移量
that.box.appendChild(that.oblock); //将生成的方块(div)追加到界面中
val = String.fromCharCode(random(65,90)); //ASCII码转换,这里由于不区分大小写,所以之选一种【65~90】
that.oblock.innerHTML = val; //将随机生成的字母设置为方块的内容
that.blockArr.push(that.oblock); //将生成的元素存入数组blockArr中去
that.keyDown() //按键检测函数
that.blockMove();//方块移动函数
},this.number); //上面提到的,根据索引确定难度后设置的时间
}
这里关键的其实就是随机字母的生成以及转换,以及要想到讲方块元素存入数组,因为后面移除的时候要找到指定的这个元素
****5.键盘检测,根据按下的键,取到对应的值,判断和方块的内容是否一致,一致就移除****
game.prototype.keyDown = function(){
that = this; //同理
document.onkeydown=function(e){ //键盘函数
e = e || window.which //这里是兼容处理
for(var i=0;i<that.blockArr.length;i++){ // 遍历数组,取到每一个创建的方块元素
if(String.fromCharCode(e.keyCode) == that.blockArr[i].innerHTML){ //判断,按下的值与数组中的某一位值相等,
若相等,则:
that.blockArr[i].remove(); //移除对应元素
that.blockArr.splice(i,1); //删除数组的对应位置,防止出现空值,不删除的话,下次按到同样的按键是,
就会找到空值,没有效果
that.text += 1; //分数+1
break; //找到后就结束循环,避免同时删除多个,只删除最下面的一个
}
}
}
}
这里主要的就是数组遍历来进行方块元素和数组的操作,还有就是循环的结束,不能让循环正常结束,除非是没有找到
对应的,不然就会删掉数组中所有满足条件的值,这样的话就感觉不太好玩了。所以要在找到第一个满足条件的时候,
删除对应元素,然后就要结束循环。
还有一个要注意的就是当删除了数组索引对应元素时,一定要删除数组中这个位置的值,不然会一直占据这个位置,而且
还是一个空值。
【可以自行尝试不写‘that.blockArr.splice(i,1)’写上后的区别,以及不加‘break’的区别】
6.方块的移动
game.prototype.blockMove = function(){
var otop = this.box.offsetHeight-this.oblock.offsetHeight; //方块距离顶部的距离
move1(this.oblock,{top:otop},()=>{ //方块到底了,游戏结束,计算总分
这里调用了封装好的move函数,有三个参数:
第一个是元素
第二个参数是要进行的动作(可以以对象形式传参)
第三个参数是一个函数,是动作完成之后要做的事
alert('总分'+this.text+'分'); //弹出弹窗,显示分数
window.history.go(0) //结束后刷新界面
});
}
4、封装的函数
****move函数,控制元素的移动以及一些属性变化****
//获取非行内样式
function getStyle(obj,attr){ //获取非行内样式,obj为对象,attr是值
if(obj.currentStyle){ //针对IE浏览器获取非行内样式,因为有兼容
return obj.currentStyle[attr];
}else{
return getComputedStyle(obj,false)[attr]; //针对非IE浏览器的非行内样式获取
};
};
****//move函数(多元素多属性的链式缓冲),会接受三个参数****
function move1(ele,json,callback){
clearInterval(ele.t);
ele.t = setInterval(() => { //在计时器每次开启后,for-in每次遍历前,创建状态,用来记录是否有属性没到终点
var onoff = true;
for(var i in json){
var iNow = parseInt(getStyle(ele,i));
var speed = 2;
speed = speed<0 ? Math.floor(speed) : Math.ceil(speed);
//如果有一个属性到目标,不一定清除计时器
//如果有一个属性没有到目标,一定不清楚计时器
//如果没有属性没到目标,一定要清楚定时器
//状态,记录,所有属性有没有到目标
//只要有属性没到终点,改变状态
if(iNow != json[i]){
onoff = false;
}
ele.style[i] = iNow + speed + "px";
}
//for-in结束后(每个属性都遍历一次后),如果状态没有被改变,意味着没有属性到终点,可以结束了
if(onoff){
clearInterval(ele.t);
callback && callback();
}
}, 30);
}
****随机数****
function random(max,min){ //就收参数,两个数值
return Math.round(Math.random()*(max-min)+min) //利用了Math.random()函数,然后取整
}
【这里封装好的move、获取非行内样式以及随机数的函数,有需要的朋友可以拿去使用,本次案例到此结束,欢迎交流。网页文件会上传到资源,有需要的可以自行下载!】