使用前端框架vue做一个小游戏

游戏介绍:随机生成4个数字,通过加减乘除,是最后的结果为24。

不足之处:

  • 随机生成的数字可能不能通过运算得出结果24,你们也可以在我的基础上进行修改。
  • 我的"确认"按钮每次只能进行两个数的运算。

闲谈:这是我这年暑假做的(挺久的),感觉还不是很成熟。很久没写了,都有些生疏了(^-^)

一、游戏布局

1.1页面布局介绍

不包含标题的情况下,大体上有三个版块:

  • 第一个版块包含了时间、解决问题数、规则
  • 第二个版块包含了运算需要的数字和字符
  • 第三个版块包含了主要的功能按钮

1.2代码

下面是我部分代码(后面有我的详细介绍)

html 复制代码
<template>
<div class="box_one">
  <div class="title">游戏</div>
  <div class="box_two">
    <div class="time_num">时间:{{ time }}</div>
    <div class="ques_num">已解决:{{number}}</div>
    <button class="tip_btn" @click="tipHandle">规则</button>
    <div  class="tip" v-if="show_tip">
      <div class="tip_c">使用给定的4个数字(每个数字必须且只能使用一次),通过加、减、乘、除和括号运算,使最终结果等于24。</div>
      <button class="con_c" @click="conHandle">关闭</button>
    </div>
    <br>
  </div>
  <div class="content">
    <div class="num">
    <div class="num_s" v-for="(item,index) in texts" :key="index">
      <button class="num_item" @click="numHandle(index)">{{ item }}</button>
    </div>
    </div>
    <div class="opener">
    <div class="opener_i" v-for="(opener,index) in openers" :key="index">
      <button class="opener_item" @click="openerHandle(index)">{{ opener }}</button>
    </div>
    </div>
    <div class="input">
    <div class="input_t">{{ str }}</div>  
    <button class="can_btn" @click="canHandle">确认</button>
    </div>
  </div>
  <div class="select">
    <div class="sel_one">
      <button class="next_btn" @click="nextHandle">下一题</button>
      <button class="clear_btn" @click="clearHandle">清空</button>
    </div>
    <div>
      <button class="start_btn" @click="startHandle">开始</button>
    </div>
    <div class="sel_two">
      <button class="restart_btn" @click="renewHandle">计时</button>
      <button class="stop_btn" @click="stopHandle">{{ btn }}</button>
    </div>

  </div>
</div>
</template>

1.3具体语法介绍

1)v-for: 这个类似于for循环,在vue中v-for 是一个用于渲染列表的指令

我这里使用v-for渲染随机生成的数字,和运算符。这样很大程度上节省了较多时间去创建更多的容器、标签。

html 复制代码
<div class="num_s" v-for="(item,index) in texts" :key="index">
      <button class="num_item" @click="numHandle(index)">{{ item }}</button>
    </div>
  • texts是我在<script>中定义的数组
  • item:当前遍历到的数组元素值
  • index:当前元素的索引位置(从0开始)
  • texts:数据源数组
  • :key="index":类似于数据库中表的主键,都是具有唯一标识。使用索引作为 key
  • {{item}}:显示当前遍历的内容

2)v-if: 用于条件渲染一块内容,这块内容只会在指令的表达式返回真值时才被渲染。

我这里用v-if来显示规则,"show_tip"这个变量初始化为false(不显示具体规则),只有点击"规则"才赋值为true

html 复制代码
<div  class="tip" v-if="show_tip">
      <div class="tip_c">使用给定的4个数字(每个数字必须且只能使用一次),通过加、减、乘、除和括号运算,使最终结果等于24。</div>
      <button class="con_c" @click="conHandle">关闭</button>
    </div>

二、游戏中按钮功能

2.1按钮功能介绍

按钮大概有7个

  • 规则:通过"show_tip"变量来确定是否显示规则。
javascript 复制代码
    tipHandle() {    //规则展开
      this.show_tip = true
    },
    conHandle() {    //规则关闭
      this.show_tip = false
    },
  • 数字/运算符:处理用户选择的数字和运算符
javascript 复制代码
    numHandle(index) {    //对数字的处理
      if (this.str1 == 0)
        this.str1 = this.texts[index]    //赋值
      else
        this.str2 = this.texts[index]    

      this.str = this.str + String(this.texts[index])    //转换成字符类型,str是要在文本框显示的
      this.texts.splice(index, 1)    //对选择的字符进行删除
    },    
    openerHandle(index) {    //对运算符的处理
      this.open = this.openers[index]    
      this.str = this.str + open
    },
  • 确认:通过用户确定的操作数和运算符,进行计算。
javascript 复制代码
canHandle() {    //计算
      if (this.str1 > 0 && this.str1 < 10 && this.str2 > 0 && this.str2 < 10 && this.open !== '') {
        switch (this.open) {
          case '+':
            this.sum = this.str1 + this.str2;
            break;
          case '-':
            this.sum = this.str1 - this.str2;
            break;
          case '*':
            this.sum = this.str1 * this.str2;
            break;
          case '/':
            this.sum = this.str1 / this.str2;
            break;
        }

        this.texts.push(this.sum)
        this.str1 = 0    //计算后数值又回到初始状态
        this.str2 = 0
        this.str = ""
        if (this.texts.length == 1 && this.sum == 24) {
          ++this.number;        //已解决的数量加1
          this.nextHandle()    //下一题(后面有介绍)
        }
      }
      else{
        alert('不能计算')
        this.clearHandle()    //清空,也就是重置(后面有介绍)
      }
    },
  }
}
  • 下一题:生成随机数
javascript 复制代码
nextHandle() {
      //this.totalTime=0;    每次点击下一题,计时要重新开始(我认为没必要)
      let i = 4;
      for (let j = 0; j < i; j++) {
        this.texts[j] = Math.floor(Math.random() * (9)) + 1
        //console.log(texts[0])
      }
      this.copy = this.texts
      this.sum = 0
      this.str = ""
      this.copy = [...this.texts]    //浅拷贝,copy这个数组是独立的,就是方便重置的。
      //this.copy = this.texts  这是引用赋值,对texts操作也会影响到copy,这里不能使用
    },
    • Math.random() * 9 生成 0-8.999... 的随机数
    • Math.floor() 向下取整得到 0-8
    • +1 后得到 1-9
  • 清空
javascript 复制代码
clearHandle() {
      this.texts = [...this.copy]    //重置回之前的状态
      this.str = ""        
      this.str1 = 0
      this.str2 = 0
    },
  • 计时:
javascript 复制代码
renewHandle() {
      if (!this.isok) return;
      if (this.isok) {
        this.totalTime++;
        const min = Math.floor(this.totalTime / 60);
        const sec = Math.floor(this.totalTime % 60);
        this.time = `${min.toString().padStart(2, '0')}:${sec.toString().padStart(2, '0')}`;
      }
      setTimeout(() => this.renewHandle(), 1000);    //1000是毫秒,也就是1秒。1秒后调用自身
    },
  • isok:来控制计时器是否启动

  • totalTime++: 累计经过的秒数

  • min: 分钟数(总秒数÷60取整)

  • sec: 秒数(总秒数÷60的余数)

  • padStart(2, '0'): 补零操作,确保显示为两位数

  • toString():转换成字符串

  • setTimeout():异步执行不会阻塞后续代码,在指定毫秒后执行回调函数

  • 暂停:

javascript 复制代码
stopHandle() {
      this.btn = this.isok ? "开始" : "暂停"
      this.isok = !this.isok;    
      this.renewHandle();    

    },
  • 开始:
javascript 复制代码
startHandle() {
      this.totalTime = 0    
      this.renewHandle()    //重新计算
      this.nextHandle()    //随机数生成
    },

2.2代码

javascript 复制代码
<script>
export default {
  data() {
    return {
      time: "00:00",
      totalTime: 0,
      isok: true,
      number: 0,
      show_tip: false,
      num: [1, 2, 3, 4, 5, 6, 7, 8, 9],
      openers: ['+', '-', '*', '/'],
      texts: [0, 0, 0, 0],
      copy: [0, 0, 0, 0],
      btn: "暂停",
      str1: 0,
      str2: 0,
      sum: 0,
      str: "",
      open: ""
    }
  },

  methods: {
    tipHandle() {
      this.show_tip = true
    },
    conHandle() {
      this.show_tip = false
    },
    renewHandle() {
      if (!this.isok) return;
      if (this.isok) {
        this.totalTime++;
        const min = Math.floor(this.totalTime / 60);
        const sec = Math.floor(this.totalTime % 60);
        this.time = `${min.toString().padStart(2, '0')}:${sec.toString().padStart(2, '0')}`;
      }
      setTimeout(() => this.renewHandle(), 1000);
    },
    nextHandle() {
      //this.totalTime=0;
      let i = 4;
      for (let j = 0; j < i; j++) {
        this.texts[j] = Math.floor(Math.random() * (9)) + 1
        //console.log(texts[0])
      }
      
      this.sum = 0
      this.str = ""
      this.copy = [...this.texts]
      //this.copy = this.texts  这是引用赋值,对texts操作也会影响到copy,这里不能使用
    },
    clearHandle() {
      this.texts = [...this.copy]
      this.str = ""
      this.str1 = 0
      this.str2 = 0
    },
    stopHandle() {
      this.btn = this.isok ? "开始" : "暂停"
      this.isok = !this.isok;
      this.renewHandle();

    },
    startHandle() {
      this.totalTime = 0
      this.renewHandle()
      this.nextHandle()
    },
    numHandle(index) {
      if (this.str1 == 0)
        this.str1 = this.texts[index]
      else
        this.str2 = this.texts[index]

      this.str = this.str + String(this.texts[index])
      this.texts.splice(index, 1)
    },
    openerHandle(index) {
      this.open = this.openers[index]
      this.str = this.str + this.open
    },
    canHandle() {
      if (this.str1 > 0 && this.str1 < 10 && this.str2 > 0 && this.str2 < 10 && this.open !='') {
        switch (this.open) {
          case '+':
            this.sum = this.str1 + this.str2;
            break;
          case '-':
            this.sum = this.str1 - this.str2;
            break;
          case '*':
            this.sum = this.str1 * this.str2;
            break;
          case '/':
            this.sum = this.str1 / this.str2;
            break;
        }

        this.texts.push(this.sum)
        this.str1 = 0
        this.str2 = 0
        this.str = ""
        if (this.texts.length == 1 && this.sum == 24) {
          ++this.number;
          this.nextHandle()
        }
      }
      else{
        alert('不能计算')
        this.clearHandle()
      }
    },
  }
}
</script>

三、源代码

全部代码,包括css

javascript 复制代码
<template>
  <div class="box_one">
    <div class="title">游戏</div>
    <div class="box_two">
      <div class="time_num">时间:{{ time }}</div>
      <div class="ques_num">已解决:{{ number }}</div>
      <button class="tip_btn" @click="tipHandle">规则</button>
      <div class="tip" v-if="show_tip">
        <div class="tip_c">使用给定的4个数字(每个数字必须且只能使用一次),通过加、减、乘、除和括号运算,使最终结果等于24。</div>
        <button class="con_c" @click="conHandle">关闭</button>
      </div>
      <br>
    </div>
    <div class="content">
      <div class="num">
        <div class="num_s" v-for="(item, index) in texts" :key="index">
          <button class="num_item" @click="numHandle(index)">{{ item }}</button>
        </div>
      </div>
      <div class="opener">
        <div class="opener_i" v-for="(opener, index) in openers" :key="index">
          <button class="opener_item" @click="openerHandle(index)">{{ opener }}</button>
        </div>
      </div>
      <div class="input">
        <div class="input_t">{{ str }}</div>
        <button class="can_btn" @click="canHandle">确认</button>
      </div>
    </div>
    <div class="select">
      <div class="sel_one">
        <button class="next_btn" @click="nextHandle">下一题</button>
        <button class="clear_btn" @click="clearHandle">清空</button>
      </div>
      <div>
        <button class="start_btn" @click="startHandle">开始</button>
      </div>
      <div class="sel_two">
        <button class="restart_btn" @click="renewHandle">计时</button>
        <button class="stop_btn" @click="stopHandle">{{ btn }}</button>
      </div>

    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      time: "00:00",
      totalTime: 0,
      isok: true,
      number: 0,
      show_tip: false,
      num: [1, 2, 3, 4, 5, 6, 7, 8, 9],
      openers: ['+', '-', '*', '/'],
      texts: [0, 0, 0, 0],
      copy: [0, 0, 0, 0],
      btn: "暂停",
      str1: 0,
      str2: 0,
      sum: 0,
      str: "",
      open: ""
    }
  },

  methods: {
    tipHandle() {
      this.show_tip = true
    },
    conHandle() {
      this.show_tip = false
    },
    renewHandle() {
      if (!this.isok) return;
      if (this.isok) {
        this.totalTime++;
        const min = Math.floor(this.totalTime / 60);
        const sec = Math.floor(this.totalTime % 60);
        this.time = `${min.toString().padStart(2, '0')}:${sec.toString().padStart(2, '0')}`;
      }
      setTimeout(() => this.renewHandle(), 1000);
    },
    nextHandle() {
      //this.totalTime=0;
      let i = 4;
      for (let j = 0; j < i; j++) {
        this.texts[j] = Math.floor(Math.random() * (9)) + 1
        //console.log(texts[0])
      }
      
      this.sum = 0
      this.str = ""
      this.copy = [...this.texts]
      //this.copy = this.texts  这是引用赋值,对texts操作也会影响到copy,这里不能使用
    },
    clearHandle() {
      this.texts = [...this.copy]
      this.str = ""
      this.str1 = 0
      this.str2 = 0
    },
    stopHandle() {
      this.btn = this.isok ? "开始" : "暂停"
      this.isok = !this.isok;
      this.renewHandle();

    },
    startHandle() {
      this.totalTime = 0
      this.renewHandle()
      this.nextHandle()
    },
    numHandle(index) {
      if (this.str1 == 0)
        this.str1 = this.texts[index]
      else
        this.str2 = this.texts[index]

      this.str = this.str + String(this.texts[index])
      this.texts.splice(index, 1)
    },
    openerHandle(index) {
      this.open = this.openers[index]
      this.str = this.str + this.open
    },
    canHandle() {
      if (this.str1 > 0 && this.str1 < 10 && this.str2 > 0 && this.str2 < 10 && this.open !='') {
        switch (this.open) {
          case '+':
            this.sum = this.str1 + this.str2;
            break;
          case '-':
            this.sum = this.str1 - this.str2;
            break;
          case '*':
            this.sum = this.str1 * this.str2;
            break;
          case '/':
            this.sum = this.str1 / this.str2;
            break;
        }

        this.texts.push(this.sum)
        this.str1 = 0
        this.str2 = 0
        this.str = ""
        if (this.texts.length == 1 && this.sum == 24) {
          ++this.number;
          this.nextHandle()
        }
      }
      else{
        alert('不能计算')
        this.clearHandle()
      }
    },
  }
}
</script>

<style>
body {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: rgba(186, 191, 94, 0.6);
}

.box_one {
  height: 800px;
  width: 850px;
  position: relative;
  background: linear-gradient(135deg, #c9df58, #97eae2, #80cbaa);
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  border: none;
  border-radius: 15px;
}

.title {
  top: 20px;
  font-size: 45px;
  font-family: "宋体";
}

.box_two {
  top: 30px;
  display: flex;
  flex-direction: row;
  column-gap: 35px;
  font-size: 35px;
  font-family: "宋体";
}

.tip_btn {
  border-radius: 10px;
  width: 75px;
  height: 45px;
  border: none;
  background-color: rgb(235, 223, 165);
  font-size: 20px;
  font-family: "宋体";
}

.tip {
  width: 350px;
  height: 100px;
  border-radius: 10px;
  background-color: rgba(238, 232, 225, 0.7);
}

.tip_c {
  font-size: 18px;
}

.con_c {
  position: relative;
  width: 65px;
  height: 45x;
  background: linear-gradient(135deg, #c9df58, #97eae2, #80cbaa);
  border: none;
  border-radius: 10px;
  right: -250px;
  bottom: 10px;
}

.content {
  position: relative;
  top: 30px;
  background-color: rgba(239, 234, 231, 0.8);
  width: 60%;
  height: 35%;
  border-radius: 20px;
  padding: 20px;
  align-items: center;
  justify-content: center;
  display: flex;
  flex-direction: column;
}

.num {
  display: flex;
  flex-direction: row;
  column-gap: 30px;
}

.opener {
  position: relative;
  top: 20px;
  display: flex;
  flex-direction: row;
  column-gap: 30px;
}

.num_s {
  height: 30%;
  width: 100%;
}

.num_item {
  background: linear-gradient(135deg, #7145da, #d688c6);
  border: none;
  border-radius: 10px;
  height: 70px;
  width: 90px;
  font-size: 25px;
  color: #e4dede;
  align-items: center;
  justify-content: center;
}

.opener_i {
  height: 30%;
  width: 100%;
}

.opener_item {
  display: flex;
  flex-direction: row;
  background: linear-gradient(135deg, #3e4163, #606c82);
  color: #f8f4f4;
  border: none;
  border-radius: 10px;
  height: 70px;
  width: 90px;
  font-size: 25px;
  align-items: center;
  justify-content: center;
}

.input {
  position: relative;
  top: 40px;
  height: 15%;
  width: 40%;
  flex-direction: row;
  column-gap: 20px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.input_t {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 80%;
  height: 35px;
  background-color: aliceblue;
  border-radius: 15px;
}

.can_btn {
  border-radius: 10px;
  position: relative;
  right: 0;
  border: none;
  height: 40px;
  background: #97eae2;
}

.select {
  position: relative;
  top: 60px;
  width: 50%;
  height: 30%;
  background-color: rgba(218, 221, 204, 0.5);
  display: flex;
  flex-direction: column;
  row-gap: 5px;
  border-radius: 20px;
  justify-content: center;
  align-items: center;
  font-size: 40px;
  font-family: '楷体';
}

.sel_one {
  display: flex;
  flex-direction: row;
  column-gap: 80px;

}

.sel_two {
  display: flex;
  flex-direction: row;
  column-gap: 80px;
}

.next_btn {
  border: none;
  width: 80px;
  height: 55px;
  background: linear-gradient(135deg, #c1e663, #c9df58, #d5d5b9);
  border-radius: 15px;
  font-size: 20px;
  font-family: '宋体';
}

.clear_btn {
  width: 80px;
  height: 55px;
  border: none;
  background: linear-gradient(135deg, #c1e663, #c9df58, #d5d5b9);
  border-radius: 15px;
  font-size: 20px;
  font-family: '送体';
}

.start_btn {
  width: 60px;
  height: 55px;
  border: none;
  background: linear-gradient(135deg, #e8895d, #ee9f85, #f2d8c3);
  border-radius: 15px;
  font-size: 20px;
  font-family: '宋体';
  color: white;
}

.restart_btn {
  width: 80px;
  height: 55px;
  border: none;
  background: linear-gradient(135deg, #80cbaa, #79ac5c, #423e34);
  border-radius: 15px;
  font-size: 20px;
  font-family: '宋体';
  color: white;
}

.stop_btn {
  width: 80px;
  height: 55px;
  border: none;
  background: linear-gradient(135deg, #80cbaa, #79ac5c, #423e34);
  border-radius: 15px;
  font-size: 20px;
  font-family: '宋体';
  color: white;
}
</style>
相关推荐
普通码农2 小时前
Vue 3 接入谷歌登录 (小白版)
前端·vue.js
Ric9702 小时前
Object.fromEntries 实操
前端
用户4099322502122 小时前
Vue3响应式系统中,对象新增属性、数组改索引、原始值代理的问题如何解决?
前端·ai编程·trae
阿明Drift2 小时前
使用 CSS `perspective` 实现 3D 卡片效果
前端·css
若安程序开发2 小时前
web京东商城前端项目4页面
前端
申阳2 小时前
Day 8:06. 基于Nuxt开发博客项目-我的服务模块开发
前端·后端·程序员
转角羊儿2 小时前
layui框架中,表单元素不显示问题
前端·javascript·layui
青浅l3 小时前
vue中回显word、Excel、txt、markdown文件
vue.js·word·excel
muyouking113 小时前
WASM 3.0 两大领域实战:SvelteKit前端新范式(完整版)
前端·wasm