开发一个应用程序,通过显示随机文本段落并记录用户输入所花费的时间,来测量用户的打字速度。
- 实现计时器和事件监听器来跟踪打字速度;
- 使用 TypeScript 处理用户输入并计算结果;
- 显示结果并向用户提供反馈。
打字速度一直是评估工程师的重要测试之一,因为更快的打字技能意味着更高效的编码。为了测试这个速度,我们将构建一个打字速度检测器。
我们将要创建的内容如下:
- 一个用于输入文本的输入框;
- 一个用于显示时间的时间元素;
- 一个用于显示每分钟字数(WPM)的元素;
- 一个用于开始测试的“开始”按钮。
HTML 与 CSS
这是一个打字速度测试 Web 应用,用户可以通过它测量自己的打字速度(以每分钟字数 WPM 为单位)。该应用包含以下主要元素:
- 一个计时器,用于显示剩余时间或计时时长;
- 一个输入区域,供用户输入测试文本;
- 一个“开始”按钮,用于启动打字测试。
<html>
<head>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
flex-direction: column;
}
.box {
text-align: center;
background: #fff;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 400px;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
}
#txt {
font-size: 18px;
margin-bottom: 20px;
font-weight: bold;
}
#inp {
width: 100%;
height: 100px;
font-size: 16px;
padding: 10px;
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 5px;
resize: none;
}
.stats {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
button {
padding: 10px 20px;
background: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:disabled {
background: #ccc;
}
button:hover {
background: #45a049;
}
</style>
</head>
<body>
<div class="box">
<h1>Typing Speed Test</h1>
<p id="txt">Press Start to begin typing</p>
<textarea id="inp" placeholder="Start typing here..." disabled></textarea>
<div class="stats">
<p id="tm">Time: 0s</p>
<p id="wpm">WPM: 0</p>
<button id="start">Start</button>
</div>
</div>
<script defer src="typing.js"></script>
</body>
</html>
在这个示例中:
.box
容器居中显示,设计简洁,包含一个标题、句子显示区、文本输入框和统计信息区。#inp
是一个textarea
,初始状态为禁用(disabled),用户点击 “开始” 按钮(#start
)后才会激活输入。#tm
(时间)和#wpm
(每分钟字数)会随着用户输入实时更新,用于跟踪用户的打字速度。
TypeScript 逻辑
这段代码创建了一个动态网格,允许用户通过更改网格大小和颜色在上面绘画。它支持通过鼠标事件进行绘画和重置网格的交互操作。
const c = document.querySelector<HTMLDivElement>(".container");
const s = document.querySelector<HTMLInputElement>('.size');
const col = document.querySelector<HTMLInputElement>('.color');
const rBtn = document.querySelector<HTMLButtonElement>('.button');
if (!c || !s || !col || !rBtn) {
throw new Error("Missing required DOM elements");
}
let sz: number = parseInt(s.value);
let d: boolean = false;
function g(): void {
c.style.setProperty("--size", sz.toString());
c.innerHTML = "";
for (let i = 0; i < sz * sz; i++) {
const b = document.createElement("div");
b.classList.add("box");
b.addEventListener('mouseenter', () => o(b));
b.addEventListener('mousedown', () => m(b));
c.appendChild(b);
}
}
function o(b: HTMLDivElement): void {
if (!d) return;
b.style.backgroundColor = col.value;
}
function m(b: HTMLDivElement): void {
b.style.backgroundColor = col.value;
}
window.addEventListener('mousedown', () => {
d = true;
});
window.addEventListener('mouseup', () => {
d = false;
});
function r(): void {
g();
}
rBtn.addEventListener('click', r);
s.addEventListener('change', () => {
sz = parseInt(s.value);
r();
});
g();
在这个示例中:
- 函数
g()
生成一个大小为 sz × sz 的网格,先清除之前的网格再添加新的单元格。 - 网格大小根据用户输入(
.size
输入框)动态更新。 - 当鼠标悬停(mouseenter)或点击(mousedown)单元格时,单元格颜色会变成用户选择的颜色(
col.value
)。 - 布尔变量
d
用来追踪鼠标是否按下,从而实现流畅绘画效果。 - 点击重置按钮(
.button
)会清空并重新创建网格,确保用户无需刷新页面即可重新开始。 - 当用户更改
.size
输入时,事件监听器s.addEventListener('change', ...)
会触发网格的更新。
将 TypeScript 文件转换为 JavaScript 文件
现在你需要将 TypeScript 文件转换为浏览器可以执行的 JavaScript 文件。你可以使用以下命令之一来完成转换。
npx tsc typing.ts
tsc typing.ts
- 命令 typing.ts 会将 typing.ts 这个 TypeScript 文件编译成 typing.js JavaScript 文件。
- 默认情况下,输出文件会放在与输入文件相同的目录下。
完整代码
<html>
<head>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
flex-direction: column;
}
.box {
text-align: center;
background: #fff;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 400px;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
}
#txt {
font-size: 18px;
margin-bottom: 20px;
font-weight: bold;
}
#inp {
width: 100%;
height: 100px;
font-size: 16px;
padding: 10px;
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 5px;
resize: none;
}
.stats {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
button {
padding: 10px 20px;
background: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:disabled {
background: #ccc;
}
button:hover {
background: #45a049;
}
</style>
</head>
<body>
<div class="box">
<h1>Typing Speed Test</h1>
<p id="txt">Press Start to begin typing</p>
<textarea id="inp" placeholder="Start typing here..." disabled></textarea>
<div class="stats">
<p id="tm">Time: 0s</p>
<p id="wpm">WPM: 0</p>
<button id="start">Start</button>
</div>
</div>
<script defer>
const txt = document.getElementById("txt");
const inp = document.getElementById("inp");
const tm = document.getElementById("tm");
const wpm = document.getElementById("wpm");
const startBtn = document.getElementById("start");
let time = 0;
let timer = null;
let isRunning = false;
const sampleText = "The quick brown fox jumps over the lazy dog"; // Sample sentence
function startTest() {
clearInterval(timer);
isRunning = true;
time = 0;
tm.textContent = "Time: 0s";
wpm.textContent = "WPM: 0";
inp.value = "";
inp.disabled = false;
inp.focus();
txt.textContent = sampleText;
timer = setInterval(() => {
time++;
tm.textContent = `Time: ${time}s`;
calculateWPM();
}, 1000);
}
function calculateWPM() {
let wordsTyped = inp.value.trim().split(/\s+/).length;
let wordsPerMinute = time > 0 ? Math.round((wordsTyped / time) * 60) : 0;
wpm.textContent = `WPM: ${wordsPerMinute}`;
}
inp.addEventListener("input", () => {
calculateWPM();
if (inp.value.toLowerCase().replace(/\s+/g, " ")
.trim() === sampleText.toLowerCase().replace(/\s+/g, " ").trim()) {
clearInterval(timer);
isRunning = false;
inp.disabled = true;
wpm.textContent = `WPM: ${Math.round((sampleText.split(" ")
.length / time) * 60)}`;
alert(`Test Completed! Your WPM: ${wpm.textContent.split(" ")[1]}`);
}
});
startBtn.addEventListener("click", startTest);
</script>
</body>
</html>