一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 2 天,点击查看活动详情。
前言
看了微软的一个课程 “Web Development for Beginners - A Curriculum” 中的一篇后,打算用 vue3.2 来重写,并小小的优化一下,当做课外作业了。
一、需求整理
目标:打字计时、记录成绩
功能点:
- 目标文字和输入文字实时对比结果
- 计时
- 成绩记录
- 切换目标文字
- 支持中英文对比
二、流程设计
- 用户键入文字开始计时,目标文字根据对比结果渲染不一样的颜色,当输入文字与目标文字全都对比成功时,提示输入完成,烟花庆祝。
- 用户输入过程中,有一个文字(包括空格)对比失败,目标文字行会在对应位置提示。
- 当用户清除全部键入文字,会重新计时。
- 切换目标文字或切换语言会清空输入状况和计时
- 成绩持久化存储
三、数据结构设计
注意,以下 JS 代码都写在 helloword 组件的 标签中
const quote = ref('') //目标文字
const willInput = ref('') // 输入文字
const successList = ref([]) // 文字对比结果列表
const language = ref('english') // 中英文切换
const quotes = reactive(priQuotes[language.value]) // 句子列表
const startTime = ref(new Date().getTime()) // 开始时间
// 知识点:vue3用ref创造基本类型的响应式变量,用reactive创造对象类型的响应式变量
更多有关 Vue3 ref 的请点击 此链接
接着我们先编写第一段逻辑,初始化,看代码:
import { onMounted, ref, watch, computed, reactive } from 'vue'
const initPage = () => {
willInput.value = '';
quote.value = quotes[Math.floor(Math.random() * quotes.length)];
successList.value = new Array(quote.value.length).fill(undefined);
}
// mounted生命周期,vue3.2里在setup script里用onMounted调用
onMounted(() => {
initPage();
})
这段代码的作用很简单,就是初始化我们的目标文字,和将我们的数据框清空,判断结果设置为未判断,很简单的做了这些事,接着我们下一步
当开始键入文字的时候,我们开始计时
// 初始化开始时间
const startTime = ref(new Date().getTime())
const spendTime = ref(0)
// 比较输入的文字和quote的文字
const compare = () => {
// 开始输入文字记录当前时间
// 被清空重新计时
if (willInput.value.length === 1 || willInput.value.length === 0) {
startTime.value = new Date().getTime();
}
// 比较当前的文字即可
for (let i = 0; i < quote.value.length; i++) {
if (i >= willInput.value.length) {
successList.value[i] = undefined;
continue;
}
if (quote.value[i] === willInput.value[i]) {
successList.value[i] = true;
} else {
successList.value[i] = false;
}
}
}
const showBoom = ref(false)
// 观测输入的文字,每次输入都会触发,成功提示
watch(successList, (nVal, oVal) => {
if (nVal.every(item => item === true)) {
spendTime.value = (new Date().getTime() - startTime.value) / 1000;
updateFastTime();
message.success(`恭喜你,用时${spendTime.value}秒完成!`);
showBoom.value = true;
initPage();
const timer = setTimeout(() => {
showBoom.value = false;
localStorage.setItem('fastestTimeMap', JSON.stringify(fastestTimeMap));
clearTimeout(timer);
}, 2000);
}
}, {
deep: true
})
const updateFastTime = () => {
const temp = fastestTimeMap[quote.value];
if (temp && temp > spendTime.value) {
fastestTimeMap[quote.value] = spendTime.value;
} else if (!temp) {
fastestTimeMap[quote.value] = spendTime.value;
}
}
const handleFaker = () => {
window.alert('不要作弊');
initPage();
}
<template>
...
<div class="quote text-item">
<span v-for="(quo, idx) of quote" :key="idx" :idx="idx"
:class="{ 'success': successList[idx] === true, 'error': successList[idx] ===
false, 'bg-error': successList[idx] === false && quo === ' ' }">
{{ quo }}</span>
</div>
<input class="keyboard-text text-item" autofocus v-model="willInput"
@input="compare" @paste.prevent="handleFaker"
:maxlength="quote.length" />
<div class="boom" v-show="showBoom">
<div class="fire"></div>
<div class="fire2"></div>
</div>
...
</template>
这里我们把目标文字拆开为单个字符,用span标签渲染,并根据输入的文字是否和当前字符相同(successList[idx]
)来动态添加 class,然后组织了粘贴行为 handleFaker
,烟花爆竹效果 showBoom
,记录成绩fastestTimeMap
;
到这里字符比较的核心逻辑已经完成,接下来我们完善这整个流程,添加切换文字和显示成绩功能
// 切换目标文字
const changeQuote = () => {
willInput.value = '';
const temp = quotes.map(item => {
if (item !== quote.value) {
return item;
}
}).filter(item => item !== undefined);
quote.value = temp[Math.floor(Math.random() * temp.length)];
successList.value = new Array(quote.value.length).fill(undefined);
}
// 切换语言版本
const changeLanguage = () => {
language.value = language.value === 'english' ? '中文' : 'english';
quotes = priQuotes[language.value];
initPage();
}
const fastestTimeMap = JSON.parse(localStorage.getItem('fastestTimeMap')) || {};
const fastestTime = computed(() => {
return fastestTimeMap[quote.value] || 0;
})
<template>
<div class="header" :style="{ background: 'rgb(190, 200, 200)',
padding: '26px 16px 16px' }">
<a-button type="dashed" ghost @click="changeQuote">换一个</a-button>
<a-button type="dashed" @click="changeLanguage">{{ language }}</a-button>
<a-button type="primary" disabled>
本题个人最快速度:<span>{{ fastestTime }}</span> 秒!
</a-button>
</div>
</template>
在这块我们添加了切换目标文字 changeQuote
和切换中英文功能 changeLanguage
,以及显示当前文字历史最快记录 fastestTime
。
在给页面元素加点样式我们就能得到一个简单的打字游戏了
希望大家来点 CR