游戏介绍:随机生成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>