JavaScript 练手小技巧:打字小游戏

 放假闲来无事,一群小屁孩想玩我的电脑。

字都不会打,还玩电脑。

用 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();

猜你喜欢

转载自blog.csdn.net/weixin_42703239/article/details/128777424