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

news2024/9/22 19:22:14

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

字都不会打,还玩电脑。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/181987.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

正则表达式-学习笔记

正则表达式&#xff08;Regular Expression&#xff09;是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff1a;a到z之间的字母&#xff09;和特殊字符&#xff08;称为“元字符”&#xff09;。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字…

企业如何利用生产制造业ERP管理系统做好采购管理?

采购对生产制造业企业而言&#xff0c;至关重要&#xff01;采购成本能够占到很多企业经营成本的60%左右&#xff0c;而所采购物料的质量直接决定了产品的质量。而在生产制造企业的采购工作中&#xff0c;经常会出现一些问题&#xff0c;比如&#xff1a;采购成本难控、采购流程…

Elasticsearch(五)--ES文档的操作(上)---写入文档

一、前言 使用ES构建搜索引擎时需要经常对文档进行操作&#xff0c;除了简单的单条文档操作&#xff0c;有时还需要进行批量操作。我们这章主要学习ES文档的增删改的操作&#xff0c;由于涉及到的代码量会比较多&#xff0c;所以分为3篇文章分别说明文档的这个三个操作。那么我…

星环科技TDH多模型统一架构VS CDH架构

CDH是Cloudera的开源平台发行版&#xff0c;通过将Hadoop与其他十几个开源项目集成&#xff0c;为企业大数据业务提供服务。 在CDH开源大数据方案中&#xff0c;是通过多个互相独立的组件提供相应的能力&#xff0c;每个场景需要一个组件独立交付&#xff0c;为了实现不同业务…

【SpringCloud】OpenFeign远程调用的基本使用

一、OpenFeign替代RestTemplateRestTemplate 存在的问题我们以前利用RestTemplate发起远程调用的代码public Order queryOrderById(Long orderId) {// 1.查询订单Order order orderMapper.findById(orderId);// 2.利用restTemplate发起http请求// 为了负载均衡使用服务名称Str…

跨境电商行业如何做好社交媒体营销?

随着互联网的快速发展,跨境电商行业也得到了快速的发展,跨境电商更是成为了当下最热门的话题之一,很多商家都想通过跨境电商平台来销售产品,但随着竞争越来越激烈,想要在众多卖家中脱颖而出,就需要从营销方面入手了&#xff0c;这就意味着卖家们需要掌握一定的营销技巧。而在现…

jsp+SSM368的药品销售配送网站系统maven

管理员登录&#xff0c;管理员通过输入用户名、密码、角色等信息进行系统登录 管理员登录进入药品销售系统可以查看&#xff1b;个人中心、用户管理、医生管理、药品信息管理、药品分类管理、订单配送管理、系统管理、订单管理等内容 个人信息&#xff0c;个人信息页面可以填写…

Swig/CPP2Java

简介 实际工程可能存在如下部分&#xff1a;业务接口需要编程高效的语言&#xff08;如Python、Java等&#xff09;&#xff0c;易于部署维护&#xff1b;而核心算法部分&#xff0c;某些场景需要高效计算&#xff0c;会使用性能高效的语言&#xff08;如C/C等&#xff09;。 …

超详细Netty入门,看这篇就够了!

简介&#xff1a; 本文主要讲述Netty框架的一些特性以及重要组件&#xff0c;希望看完之后能对Netty框架有一个比较直观的感受&#xff0c;希望能帮助读者快速入门Netty&#xff0c;减少一些弯路。 前言 本文主要讲述Netty框架的一些特性以及重要组件&#xff0c;希望看完之后…

一起自学SLAM算法:10.2 VINS算法

连载文章&#xff0c;长期更新&#xff0c;欢迎关注&#xff1a; 不管是激光SLAM还是视觉SLAM&#xff0c;由于传感器采样率、传感器测量精度、主机计算力等因素的限制&#xff0c;在高速运动状态下定位追踪极易丢失。虽然轮式里程计能为激光SLAM系统提供短期运动预测以避免高速…

记录一次ubuntu进入不了界面的恢复记录

能说服一个人的从来不是道理&#xff0c;而是南墙&#xff1b;能点醒一个人的从来不是说教&#xff0c;而是磨难 一、问题描述 1、 卸载Python之后&#xff0c;ubuntu启动进入黑屏tty界面无法联网&#xff0c;无法进入桌面 2、 进入到界面之后没有网络&#xff0c;网络中或者右…

【分析向】没有三级缓存会导致什么?

通过上篇&#xff08;【实践向】当移除了三级缓存…… &#xff09;的实践&#xff0c;我们得出的结论是&#xff1a;如果不存在代理对象&#xff0c;二级缓存就可以解决循环依赖性的问题&#xff0c;但是当存在代理对象的时候&#xff0c;二级缓存则无法完全解决循环依赖&…

机器自动翻译古文拼音 - 十大宋词 - ALL

机器自动翻译古文拼音 - 十大宋词 - 雨霖铃寒蝉凄切 柳永https://mp.csdn.net/mp_blog/creation/editor/128779245机器自动翻译古文拼音 - 十大宋词 - 江城子乙卯正月二十日夜记梦 苏轼https://mp.csdn.net/mp_blog/creation/editor/128779156机器自动翻译古文拼音 - 十大宋词 …

0基础小白十分钟入门人工智能强化学习(附有实战源码)

强化学习概述 1.1 强化学习的学习任务目标 强化学习&#xff08;Reinforcement Learning, RL&#xff09;&#xff0c;用官话讲&#xff0c;是机器学习的范式和方法论之一&#xff0c;用于描述和解决智能体&#xff08;agent&#xff09;在与环境的交互过程中通过学习策略以达成…

Mybatis-Plus 乐观锁与代码生成器

目录 乐观锁 问题引入 乐观锁实现思路 实现步骤 代码生成器 代码生成器分析 代码生成器实现 乐观锁 问题引入 业务并发现象带来的问题:秒杀 假如有100个商品或者票在出售&#xff0c;为了能保证每个商品或者票只能被一个人购买&#xff0c;如何保证不会出现超买或者重复…

记一次nginx崩溃事件

一、事件描述 2023年春节复工第一天&#xff0c;项目组同事反馈说业务系统中图像处理代理Nginx服务于1月23日发生崩溃&#xff0c;完成了重启操作&#xff0c;检查nginx的日志有如下报错&#xff1a; 2023/01/23 11:07:07 [crit] 3237#3237: *2253009 pwritev() "/var/c…

网络编程-----(Socket编程TCP)

在咱们的TCP API中&#xff0c;也是主要是涉及到两个类: 1)ServerSocket:主要是给TCP服务器来进行使用的&#xff1b; 2)Socket:我们既需要给客户端来进行使用&#xff0c;也需要给服务器来进行使用&#xff1b; 这样就是说我们是不需要使用专门的类来进行表示传输的包&#x…

Java学习之抽象模板模式

目录 一、基本介绍 二、模板设计模式能解决的问题 三、最佳实践 一、AA类 二、BB类 三、main方法实现 四、提取相同语句 五、建立继承关系 父类-Template 子类-AA类 子类-BB类 六、运行中的动态绑定机制 一、基本介绍 抽象类体现的就是一种模板模式的设计&#xff…

【Git】概述

目录 1.1 是什么 介绍 历史时间轴 版本控制工具 1.2 能干嘛 作用 Git工作机制 代码托管中心 集中式版本控制系统 分布式版本控制系统 1.3 去哪下 命令行工具&#xff1a;Git for windows 操作系统中可视化工具&#xff1a;TortoiseGit(了解) GitHub网站 1.1 是什…

带你走进Java8新特性Stream流的小世界

目录 一. 什么是流&#xff08;Stream&#xff09; 1.1 流的定义 1.2 流的特点 1.3 操作流 1.4 创建流 二. 流的中间操作 2.1 流的筛选与切片 2.1.1 filter 2.1.2 limit 2.1.3 skip 2.1.4 distinct 2.2 流的映射 2.2.1 map 2.2.2 flatMap 2.3 流的排序 2.3.1 s…