父爱如山-用vue3给女儿做做个10以内加减法练习工具--3/1000

我是天元,立志做1000个有趣的项目的前端。公众号:cssandjs

如果你喜欢的话,请点赞,收藏,转发

背景

我可爱的女儿,现在正在幼儿园中班就读。她已经开始学习10以内的加减法了,为了帮助她学习与成长,所以我决定给她写一个练习工具。

image.png

需求分析

  • 鉴于我们的用户比较低龄,所以不适宜采用负反馈。以往各种工具中的失败,错误都不适宜出现。
  • 考虑孩子识字不多,应该加入语音系统。考虑个人预算不多,语音系统应该免费
  • 每次10道题目,提供10以内的加减法计算。每次点击错误,给予鼓励的语音提示。让孩子重新再思考下。

相关页面UI

image.png

分为开始界面,答题界面,以及成功页面(无失败页面)

编码部分

因为页面不多,没有使用路由的必要,所以该项目仅使用组件切换的方式来展示效果。

新建一个vue3项目

npm create vue@latest    

因为不需要复杂的内容,所有选项均为否

为了界面更可爱一点,修改 src/assets/main.css,修改#app


#app {
  margin: 0 auto;
  font-weight: normal;
  background: #83d4cd;
  min-height: 100vh;
}

定义状态 src/App.vue

在App.vue中定义一个页面状态来决定显示哪个页面,默认为start页面

<script setup>
import { ref } from 'vue'
import StartPage from './components/StartPage.vue'
import MathPage from './components/MathPage.vue'
import SuccessPage from './components/SuccessPage.vue'

const pageStatus = ref('start')

function onChangeStatus(status) {
  pageStatus.value = status
}

</script>

<template>
  <StartPage @onChangeStatus="onChangeStatus" v-if="pageStatus === 'start'" />
  <MathPage @onChangeStatus="onChangeStatus" v-else-if="pageStatus === 'math'" />
  <SuccessPage @onChangeStatus="onChangeStatus" v-if="pageStatus === 'success'" />
</template>

<style scoped></style>

这里定义了三个组件来展示不同的页面,同时根据pageStatus来切换。我们也定义了一个切换pageStatus的方法,便于之后在页面中的修改。

开始页面-src/components/StartPage.vue

开始页面很简单,只需要一个简单的头图+开始按钮即可。先用ai生成一个背景图,保存在src/assets/banner.jpg
image.png

<script setup>
</script>
<template>
    <div class="banner">
        <img src="../assets/banner.png" alt="logo" />
    </div>
    <div class="start-button">start</div>

</template>

<style scoped>
.banner {
    img {
        width: 100%;
        display: block;

    }

}

.start-button {
    width: 80%;
    height: 100px;
    margin: 40px auto;
    border-radius: 40px;
    background-color: #ffe2a6;
    display: flex;
    color: orange;
    justify-content: center;
    align-items: center;
    font-size: 32px;
    font-weight: bold;
    cursor: pointer;

}
</style>

效果如下

image.png

绑定点击逻辑

给start按钮一个click事件,然后emit到App.vue页面

src/components/StartPage.vue修改如下

<script setup>
import { defineEmits } from 'vue';
const emit = defineEmits(['onChangeStatus']);
const onStart = () => {
    emit('onChangeStatus', 'math');
};

</script>
<template>
    ...
    <div class="start-button" @click="onStart">start</div>

</template>

现在我们绑定了一个点击事件,点击时将修改App.vue中的status,以达到切换组件的目的。

题目页面-src/components/MathPage.vue

题目页面,需要计算10以内的加减法,所以我们分析具体情况

image.png

因为不考虑0的情况,所以加法的结果必然为2-10,减法的第一个数字必然也是2-10。所以我们可以先生成一个2-10的随机数作为结果,然后再生成一个1-第一个数字的随机数作为第二个数。再根据加减法,调换下相关数字的顺序即可。

在题目页面创建一个函数

function createMath() {
    //  生成2-10的随机数
    let result = Math.floor(Math.random() * 8) + 2
    const operator = Math.random() > 0.5 ? '+' : '-'; // 生成规则
    const firstNumber = Math.floor(Math.random() * (result - 1)) + 1
    let secondNumber = result - firstNumber
    if (operator === '+') {
        return {
            firstNumber,
            secondNumber,
            operator,
            result
        }
    } else {
        return {
            firstNumber: result,
            secondNumber: firstNumber,
            operator,
            result: secondNumber
        }

    }
}
}

然后我们把相关的初始化逻辑补充进去,生成10道题并存储起来。因为题目列表在页面中是不需要渲染的,所以这里不需要通过ref初始化。设定一个current变量,用来存储当前显示的题目和结果。

<script setup>
import { ref, onMounted, defineEmits} from 'vue'
const emit=defineEmits(['onChangeStatus'])
const mathList = []
const current = ref({})

function createMath() {
    ...
}

onMounted(() => {
    for (let i = 0; i < 10; i++) {
        mathList.push(createMath())
    }
    current.value = mathList.pop()

})
</script>

接下来我们要创建点击函数,当点击的结果与currentresult相同即代表是正确的,同时从题目列表中拿一个新的题目出来。

当列表中没有新的题目,即代表已经成功闯关,切换状态到成功。


function answerClick(answer) {
    if (mathList.length === 0) {
        emit('onChangeStatus', 'success')
        return
    }
    if (answer === current.value.result) {
        console.log('right')
        current.value = mathList.shift()
    } else {
        console.log('wrong')
    }

}

最后书写页面及绑定answerClick

<template>
    <div class="math">
        <div class=" box">{{ current.firstNumber }}</div>
        <div class=" box">{{ current.operator }}</div>
        <div class=" box">{{ current.secondNumber }}</div>
        <div class=" box">=</div>
        <div class=" box">?</div>
    </div>
    <div class="answer">
        <div class="answer-button" @click="answerClick(i)" v-for="i in 10">
            {{ i }}</div>
    </div>

</template>
<style scoped>
.math {
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 32px;
    font-weight: bold;
    gap: 10px;
    padding: 100px 0;
    font-weight: bold;


}

.box {
    width: 60px;
    border-radius: 10px;
    background: #ffe2a6;
    line-height: 60px;
    text-align: center;
    color: #8ec1db;
}


.answer {
    padding: 0 20px;
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 10px;
}

.answer-button {
    width: 60px;
    height: 60px;
    border-radius: 10px;
    background: #fff;
    line-height: 60px;
    text-align: center;
    color: #80ccc6;
    font-size: 24px;
    font-weight: bold;
    cursor: pointer;
    box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.25);

}
</style>

页面效果如下
image.png

为点击事件添加音效

相关的音频资源,可以通过在线的文字转声音来获取,这里不做具体介绍。相关素材建议长度为3s。另外准备正确错误的相关图标,可以从iconfont上下载。
image.png

导入相关资源,并在触发点击事件时执行

<template>
   
    <div class="tooptip" v-if="toolTip === 'right'">
        <img src="../assets/right.png" alt="right" />
    </div>
    <div class="tooptip" v-if="toolTip === 'wrong'">
        <img src="../assets/wrong.png" alt="wrong" />
    </div>

</template>


<script setup>
...
import rightAudioSource from '../assets/right.mp3'
import wrongAudioSource from '../assets/wrong.mp3'

const toolTip = ref(null)

const rightAudio = new Audio(rightAudioSource)
const wrongAudio = new Audio(wrongAudioSource)


function changeToolTip(value) {
    let audio
    toolTip.value = value
    if (value === 'right') {
        audio = rightAudio
    } else if (value === 'wrong') {
        audio = wrongAudio
    }
    audio.play()

    setTimeout(() => {
        toolTip.value = null
    }, 3000)
}

</script>

制作结果页 src/components/SuccessPage.vue

最后再制作最终的一个简单的结果页即可。

<template>
    <div class="container">

        <img src="../assets/success.png" alt="right" />
    </div>
    <div class="restart" @click="restart">
        再来一次
    </div>
</template>

<script setup>
import { defineEmits, onMounted } from 'vue';
import successAudioSource from '../assets/success.mp3';
const emit = defineEmits(['onChangeStatus']);
const successAudio = new Audio(successAudioSource);
const restart = () => {
    emit('onChangeStatus', 'start');
};

onMounted(() => {
    successAudio.play();
});
</script>

<style scoped>
.container {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;

}

.container img {
    width: 100%;
    display: block;
}


.restart {
    width: 80%;
    height: 100px;
    margin: 40px auto;
    border-radius: 40px;
    background-color: #ffe2a6;
    display: flex;
    color: orange;
    justify-content: center;
    align-items: center;
    font-size: 32px;
    font-weight: bold;
    cursor: pointer;

}
</style>

最终效果

image.png

github 地址

https://github.com/tinlee/1000-project-demo/tree/main/vue3-add-and-subtract-method-within-10

我是天元,立志做1000个有趣的项目的前端。

如果你喜欢的话,请点赞,收藏,转发

评论领取父爱如山勋章

  • 51
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值