放假闲来无事,一群小屁孩想玩我的电脑。
字都不会打,还玩电脑。
用 js 写一个打字游戏,打不到 100 分,就不要玩我的电脑~~~!!!
整体界面如下所示,一切从简~
HTML 结构
<div id="box" class="box">
<div class="tips">
击中数量:<span id="scoreSpan" style="margin-right: 20px;">0</span>
失误:<span id="missSpan">0</span>
</div>
<div class="container" id="container">
</div>
<!-- 游戏结束标签 -->
<div class="gameover">
<h1>游戏结束</h1>
<div class="overBtn">
<button type="button" id="btn">重新开始</button>
</div>
</div>
</div>
div.container 是字母出现的区域,相对定位。
字母是 JS 动态生成的 span 标签,全部绝对定位。
div.gameover 是游戏结束时的画面,默认是隐藏的。当游戏结束的时候,给 div.box 添加一个类 over,才让 div.gameover 显示出来。
具体样式见下 CSS 样式部分。
CSS 样式
*{
margin: 0;
padding: 0;
}
div.box{
width: 100vw;
height: 100vh;
position: relative;
background:center center url("../images/mm.jpg") no-repeat;
background-size: cover;
}
.tips{
position: absolute;
left:20px;
top:20px;
font-size: 20px;
line-height: 40px;
z-index: 2;
}
.tips span{
font-size: 30px;
color: #ff6600;
vertical-align: middle;
}
.container{
width: 100%;
height: 100vh;
position: relative;
overflow: hidden;
background: rgba(255,255,255,0.5);
}
.container span.zm{
font-size: 40px;
display: inline-block;
padding: 5px 10px;
height: 80px;
line-height: 80px;
overflow: hidden;
position: absolute;
}
span.zm.shoot{
animation: shootAni 0.2s;
}
@keyframes shootAni {
0%{
opacity: 1;
transform: scale(1);
}
100%{
opacity: 0;
transform: scale(1.5);
}
}
.gameover{
position: absolute;
left: 0;
top:0;
background: rgba(0,0,0,0.5);
bottom:0;
right:0;
text-align: center;
display: none;
}
.over .gameover{
display: block;
}
.over .gameover h1{
padding-top: 40vh;
margin-bottom: 40px;
}
.over .gameover button{
cursor: pointer;
width: 100px;
height: 50px;
}
</style>
JavaScript 部分
字母是 26 个字母随机出现,因此利用一个字符串存储字母。
let zmStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
字母是随机生成的 span,span的内容就是随机字母。但是,字母不能跟已有的字母重复,因此要做一个重复性的判断。
判断的方式:先把已有的字母收集起来,形成一个字符串。随机字母的时候,就判断是否已经存在。如果存在,就重新随机选取字母。如果不存在,ok,就把这个字母放入span 中。
// 随机生成字母
let zmInStr =""; // 已有的字母
for(let i=0; i<=container.children.length-1; i++){
zmInStr += container.children[i].innerText ;
}
let zmNow = zmStr[ Math.floor( Math.random()*zmStr.length) ];
// 防止字母重复
while( zmInStr.indexOf(zmNow) >= 0){
zmNow = zmStr[ Math.floor( Math.random()*zmStr.length) ]
}
span.innerText = zmNow;
每个生成的字母,也就是 span,它的位置,速度都是随机的。
span.style.fontSize = (Math.random()*50+30)+"px";
span.style.color = fontColorArr[Math.floor(fontColorArr.length*Math.random())];
// 字母出现的位置
span.style.left = (80+Math.random()*(container.offsetWidth-160)) + "px";
span.style.top = `${60}px` ;
// 每个字母设置下落速度
span.speed = spDi+Math.random()*spCtr;
// 每个字母的下落距离
span.dis = 0;
当字母被击中,会执行一个 animation 动画。动画结束后,该字母span标签要被删除。所以,字母的span标签需要监听 animationend 事件。
// 添加动画事件
span.addEventListener("animationend",function () {
// 当 animation 动画结束后,移除该字母
container.removeChild(span);
});
下坠动画部分。
利用的是 requestAnimationFrame ,每次执行动画先遍历所有字母 span。
获取每个 span 的速度 speed 和 移动的距离 dis。在当前 dis 上添加 speed 值,实现位置变化。
// 获取每个span的速度和位置
let speed = Number(span.speed);
let dis = Number(span.dis);
span.style.top = `${dis+speed}px`;
span.dis = `${dis+speed}`;
还要判断字母是否移动到了屏幕之外,这个时候说明字母没有被击中。要添加失误分。
// 判断字母是否在外面。
if( Number(span.dis) > Number(container.offsetHeight)+10){
container.removeChild(span);
createSpan();
missScore++;
missSpan.innerText = missScore;
}
当失误分超过10分的时候,游戏结束。
// 判断游戏是否结束:失误超过10次
if(missScore>=10){
cancelAnimationFrame(req);
box.classList.add("over");
return ;
}
当用户击打键盘的时候,要判断按下的键是否在已有的字母中。
因此,要遍历字母 span 标签,判断按键是否跟其中的一个一致。
有,则这个字母被击中,添加击中动画 shoot,速度归零,再创建一个新的字母补位。得分+1 。
// 添加事件
document.addEventListener("keyup",function (e) {
console.info( e.code );
let spans = container.getElementsByTagName("span");
// 判断按键
for(let i=0; i<spans.length ; i++){
// 击中了字母:按下了正确的字母键
if( "Key"+spans[i].innerText == e.code ){
spans[i].classList.add("shoot"); // 击中字母
spans[i].speed = 0; // 被击中的字母不再移动
createSpan(); // 再生成一个字母
// 得分
score++;
scoreSpan.innerText = score ;
break;
}
}
});
完整 JavaScript 代码如下:
let box = document.getElementById("box");
let container = document.getElementById("container");
let missSpan = document.getElementById("missSpan");
let scoreSpan = document.getElementById("scoreSpan");
let btn = document.getElementById("btn");
let numZM = 5;
let score = 0 ; // 得分
let missScore = 0; // 失误
let zmStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let spCtr = 1; // 速度控制变量
let spDi = 0.5;
let req ; // 动画id
let fontColorArr = ["#ba300c","#057a7d","#b724c7","#8cc111"];
// 创造一个字母
function createSpan(){
let span = document.createElement("span");
span.className = "zm";
// 随机生成字母
let zmInStr =""; // 已有的字母
for(let i=0; i<=container.children.length-1; i++){
zmInStr += container.children[i].innerText ;
}
let zmNow = zmStr[ Math.floor( Math.random()*zmStr.length) ];
// 防止字母重复
while( zmInStr.indexOf(zmNow) >= 0){
zmNow = zmStr[ Math.floor( Math.random()*zmStr.length) ]
}
span.innerText = zmNow;
container.appendChild(span);
span.style.fontSize = (Math.random()*50+30)+"px";
span.style.color = fontColorArr[Math.floor(fontColorArr.length*Math.random())];
// 字母出现的位置
span.style.left = (80+Math.random()*(container.offsetWidth-160)) + "px";
span.style.top = `${60}px` ;
// 每个字母设置下落速度
span.speed = spDi+Math.random()*spCtr;
// 每个字母的下落距离
span.dis = 0;
// 添加动画事件
span.addEventListener("animationend",function () {
// 当 animation 动画结束后,移除该字母
container.removeChild(span);
});
}
// 移动函数
function move(){
let spans = container.children;
for(let i=0; i<spans.length; i++){
let span = spans[i];
// 获取每个span的速度和位置
let speed = Number(span.speed);
let dis = Number(span.dis);
span.style.top = `${dis+speed}px`;
span.dis = `${dis+speed}`;
// 判断字母是否在外面。
if( Number(span.dis) > Number(container.offsetHeight)+10){
container.removeChild(span);
createSpan();
missScore++;
missSpan.innerText = missScore;
}
}
// 判断游戏是否结束:失误超过10次
if(missScore>=10){
cancelAnimationFrame(req);
box.classList.add("over");
return ;
}
// 动画循环
req = requestAnimationFrame(move);
}
// 初始化
function initGame(){
container.innerHTML = "";
score = 0;
scoreSpan.innerText = score ;
missScore = 0;
missSpan.innerText = missScore;
box.classList.remove("over");
// 初始生成字母
for(let i=0; i<numZM; i++){
createSpan()
}
move();
}
// 添加事件
document.addEventListener("keyup",function (e) {
console.info( e.code );
let spans = container.getElementsByTagName("span");
// 判断按键
for(let i=0; i<spans.length ; i++){
// 击中了字母:按下了正确的字母键
if( "Key"+spans[i].innerText == e.code ){
spans[i].classList.add("shoot"); // 击中字母
spans[i].speed = 0; // 被击中的字母不再移动
createSpan(); // 再生成一个字母
// 得分
score++;
scoreSpan.innerText = score ;
break;
}
}
});
// 重新开始游戏:
btn.addEventListener("click",function(){
initGame()
});
// 启动游戏
initGame();