英文 | https://www.geeksforgeeks.org/design-a-typing-speed-test-game-using-javascript/?ref=leftbar-rightbar
翻译 | web前端开发(ID:web_qdkf)
打字测试的目的是找出在给定的时间内打字的速度。我们将使用JavaScript设计打字游戏,该游戏提出了一个简单的打字挑战,并通过计算每分钟字符数(CPM),每分钟字数(WPM)和键入字符的准确性来找到键入性能。
游戏会显示一系列必须在指定的时限内尽快键入的报价。较高的键入速度将显示较高的WPM值。在键入过程中将相应地标记输入错误的字符。
我们将首先创建HTML布局,使用CSS设置样式,然后使用JavaScript编写逻辑。
HTML布局: HTML布局定义了将在页面上显示的元素结构。这包括:
标头部分:此部分显示当前键入会话的统计信息。这包括剩余时间的显示,错误数量,准确性,WPM和CPM。
引用部分:此部分显示必须在输入区域中键入的当前文本。
输入区域:此部分包含必须在其中键入文本的输入区域。
重新启动按钮:这是重新启动按钮,一旦时间用完并且游戏结束,就会显示该按钮。
代码:
<html lang="en">
<head>
<title>Simple Speed Typer</title>
<!-- link the CSS file here -->
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<div class="heading">
Simple Speed Typing
</div>
<div class="header">
<div class="wpm">
<div class="header_text">WPM</div>
<div class="curr_wpm">100</div>
</div>
<div class="cpm">
<div class="header_text">CPM</div>
<div class="curr_cpm">100</div>
</div>
<div class="errors">
<div class="header_text">Errors</div>
<div class="curr_errors">0</div>
</div>
<div class="timer">
<div class="header_text">Time</div>
<div class="curr_time">60s</div>
</div>
<div class="accuracy">
<div class="header_text">% Accuracy</div>
<div class="curr_accuracy">100</div>
</div>
</div>
<div class="quote">
Click on the area below to start the game.
</div>
<textarea class="input_area"
placeholder="start typing here..."
oninput="processCurrentText()"
onfocus="startGame()">
</textarea>
<button class="restart_btn"
onclick="resetValues()">
Restart
</button>
</div>
<!-- link the JavaScript file here -->
<script src="game.js">
</script>
</body>
</html>
标题部分使用flex布局显示。
给每个元素足够的填充和边距。
每个元素的文本大小应使用户在玩游戏时易于阅读。
定义了两个附加类来表示正确或错误键入的字母。这些类将在需要时动态添加或删除。
body {
background-color: #fe9801;
color: black;
text-align: center;
}
.container {
display: flex;
flex-direction: column;
align-items: center;
}
.heading {
margin-bottom: 20px;
font-size: 3rem;
color: black;
}
.header {
display: flex;
align-items: center;
}
.timer, .errors, .accuracy,
.cpm, .wpm {
background-color: #ccda46;
height: 60px;
width: 70px;
margin: 8px;
padding: 12px;
border-radius: 20%;
box-shadow: black 5px 8px 5px;
}
.cpm, .wpm {
display: none;
}
.header_text {
text-transform: uppercase;
font-size: 0.6rem;
font-weight: 600;
}
.curr_time, .curr_errors,
.curr_accuracy, .curr_cpm,
.curr_wpm {
font-size: 2.75rem;
}
.quote {
background-color: #ccda46;
font-size: 1.5rem;
margin: 10px;
padding: 25px;
box-shadow: black 5px 8px 5px;
}
.input_area {
background-color: #f5f5c6;
height: 80px;
width: 40%;
font-size: 1.5rem;
font-weight: 600;
margin: 15px;
padding: 20px;
border: 0px;
box-shadow: black 5px 8px 5px;
}
.restart_btn {
display: none;
background-color: #326765;
font-size: 1.5rem;
padding: 10px;
border: 0px;
box-shadow: black 5px 8px 5px;
}
.incorrect_char {
color: red;
text-decoration: underline;
}
.correct_char {
color: darkgreen;
}
// define the time limit
let TIME_LIMIT = 60;
// define quotes to be used
let quotes_array = [
"Push yourself, because no one else is going to do it for you.",
"Failure is the condiment that gives success its flavor.",
"Wake up with determination. Go to bed with satisfaction.",
"It's going to be hard, but hard does not mean impossible.",
"Learning never exhausts the mind.",
"The only way to do great work is to love what you do."
];
// selecting required elements
let timer_text = document.querySelector(".curr_time");
let accuracy_text = document.querySelector(".curr_accuracy");
let error_text = document.querySelector(".curr_errors");
let cpm_text = document.querySelector(".curr_cpm");
let wpm_text = document.querySelector(".curr_wpm");
let quote_text = document.querySelector(".quote");
let input_area = document.querySelector(".input_area");
let restart_btn = document.querySelector(".restart_btn");
let cpm_group = document.querySelector(".cpm");
let wpm_group = document.querySelector(".wpm");
let error_group = document.querySelector(".errors");
let accuracy_group = document.querySelector(".accuracy");
let timeLeft = TIME_LIMIT;
let timeElapsed = 0;
let total_errors = 0;
let errors = 0;
let accuracy = 0;
let characterTyped = 0;
let current_quote = "";
let quoteNo = 0;
let timer = null;
获取文本
引号已用作必须输入文本游戏。每个引用都是从预定义数组中一个接一个地获取的。变量会跟踪当前的报价索引,并在每次请求新的报价时对其进行递增。将字符拆分为元素
文本中的每个字符都分为一系列<span>元素。这使得可以根据用户是否正确键入了每个字符来分别更改其颜色。这些元素被附加到变量中quote_text。
function updateQuote() {
quote_text.textContent = null;
current_quote = quotes_array[quoteNo];
// separate each character and make an element
// out of each of them to individually style them
current_quote.split('').forEach(char => {
const charSpan = document.createElement('span')
charSpan.innerText = char
quote_text.appendChild(charSpan)
})
// roll over to the first quote
if (quoteNo < quotes_array.length - 1)
quoteNo++;
else
quoteNo = 0;
}
获取输入框的当前值输入区域的value属性用于获取用户输入的当前文本。这被拆分为一个字符数组以与报价文本进行比较。这存储在中curr_input_array。
为引用文本的字符着色,根据所输入引用的字符是否正确,将其显示为“红色”或“绿色”。这是通过选择我们之前创建的报价的span元素并循环遍历它们来完成的。然后,元素将根据是否与键入的文本匹配来应用上面创建的类。
计算错误和准确性
每次用户在输入过程中输入错误时,errors变量都会递增。这用于通过将正确键入的字符数除以用户键入的字符总数来计算精度值。移至下一个引号
当输入文本的长度与引号文本长度匹配时,将updateQuote()调用该函数,该函数将更改引号并清除输入区域。总错误数也将更新以用于下一个报价。
function processCurrentText() {
// get current input text and split it
curr_input = input_area.value;
curr_input_array = curr_input.split('');
// increment total characters typed
characterTyped++;
errors = 0;
quoteSpanArray = quote_text.querySelectorAll('span');
quoteSpanArray.forEach((char, index) => {
let typedChar = curr_input_array[index]
// character not currently typed
if (typedChar == null) {
char.classList.remove('correct_char');
char.classList.remove('incorrect_char');
// correct character
} else if (typedChar === char.innerText) {
char.classList.add('correct_char');
char.classList.remove('incorrect_char');
// incorrect character
} else {
char.classList.add('incorrect_char');
char.classList.remove('correct_char');
// increment number of errors
errors++;
}
});
// display the number of errors
error_text.textContent = total_errors + errors;
// update accuracy text
let correctCharacters = (characterTyped - (total_errors + errors));
let accuracyVal = ((correctCharacters / characterTyped) * 100);
accuracy_text.textContent = Math.round(accuracyVal);
// if current text is completely typed
// irrespective of errors
if (curr_input.length == current_quote.length) {
updateQuote();
// update total errors
total_errors += errors;
// clear the input area
input_area.value = "";
}
}
重置所有值
在开始新游戏之前,所有值都将重置为其默认值。我们创建一个名为的不同函数resetValues()来处理此问题。更新文本
准备新的文本并通过调用该updateQuote()函数显示它。创建一个新计时器
计时器会跟踪剩余的秒数并将其显示给用户。它是使用setInterval()重复调用updateTimer()下面定义的函数的方法创建的。在创建新计时器之前,使用清除先前的计时器实例clearInterval()。
function startGame() {
resetValues();
updateQuote();
// clear old and start a new timer
clearInterval(timer);
timer = setInterval(updateTimer, 1000);
}
function resetValues() {
timeLeft = TIME_LIMIT;
timeElapsed = 0;
errors = 0;
total_errors = 0;
accuracy = 0;
characterTyped = 0;
quoteNo = 0;
input_area.disabled = false;
input_area.value = "";
quote_text.textContent = 'Click on the area below to start the game.';
accuracy_text.textContent = 100;
timer_text.textContent = timeLeft + 's';
error_text.textContent = 0;
restart_btn.style.display = "none";
cpm_group.style.display = "none";
wpm_group.style.display = "none";
}
更新时间值会更新
所有跟踪时间的变量。该timeLeft值减小,该timeElapsed值增大,并且计时器文本更新为当前剩余时间。完成游戏
达到时间限制时将触发此部分。它调用finishGame()下面定义的函数,从而完成游戏。
function updateTimer() {
if (timeLeft > 0) {
// decrease the current time left
timeLeft--;
// increase the time elapsed
timeElapsed++;
// update the timer text
timer_text.textContent = timeLeft + "s";
}
else {
// finish the game
finishGame();
}
}
删除计时器
之前创建的计时器实例将被删除。显示重新启动游戏的文本和按钮
显示给用户的引用文本将更改为表示游戏结束的文本。通过将显示属性设置为“阻止”,还可以显示“重新启动”按钮。计算当前会话的CPM和WPM
每分钟字符数(CPM)通过将键入的字符总数除以经过的时间,然后将结果乘以60来计算。四舍五入以防止小数点。
每分钟字数(WPM)的计算方法是将CPM除以5,然后将结果乘以60。5表示每个字的平均字符数。四舍五入以防止出现小数点。
function finishGame() {
// stop the timer
clearInterval(timer);
// disable the input area
input_area.disabled = true;
// show finishing text
quote_text.textContent = "Click on restart to start a new game.";
// display restart button
restart_btn.style.display = "block";
// calculate cpm and wpm
cpm = Math.round(((characterTyped / timeElapsed) * 60));
wpm = Math.round((((characterTyped / 5) / timeElapsed) * 60));
// update cpm and wpm text
cpm_text.textContent = cpm;
wpm_text.textContent = wpm;
// display the cpm and wpm
cpm_group.style.display = "block";
wpm_group.style.display = "block";
}
本文分享自微信公众号 - web前端开发(web_qdkf)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
来源:oschina
链接:https://my.oschina.net/u/4584973/blog/4395378