英文字母打字速度测试游戏代码
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <title>CodePen - Alphabet Speed Test </title> <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet"> <style> *, *::before, *::after { box-sizing: border-box; } html { font-family: \'Source Sans Pro\', -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, Oxygen, Ubuntu, Cantarell, \'Open Sans\', \'Helvetica Neue\', sans-serif; } body { align-items: center; background-color: #b24592; background-image: linear-gradient(166deg, blueviolet, darkcyan); display: flex; justify-content: center; margin: 0; min-height: 100vh; padding: 0; } .container { background-color: rgba(255, 255, 255, 0.25); border-radius: 8px; box-shadow: 0 1px 6px rgba(0, 0, 0, 0.15); max-width: 700px; padding: 2rem 3rem; width: 90%; } .game-container[data-state="pre-game"] .timer { opacity: 0; } .game-container[data-state="pre-game"] .restart-button, .game-container[data-state="pre-game"] .restart-button:hover, .game-container[data-state="pre-game"] .restart-button:focus { background-color: #fff; color: #333; opacity: .6; } .highscore-container { font-size: .9rem; margin: 1rem 0; } .timer { margin: 1rem 0; transition: opacity .1s ease-in-out; } .letter__container { display: flex; flex-wrap: wrap; margin-left: -.25rem; width: calc(100% + .5rem); } .letter { --background-color: rgba(255, 255, 255, .4); background-color: var(--background-color); border-radius: 3px; color: #333; display: block; font-size: 1.5rem; height: 2em; line-height: 2em; margin: .25rem; overflow: hidden; position: relative; text-align: center; text-transform: uppercase; width: 2em; } .letter[data-state=active]::before { transform: scaleX(1); } .letter::before { background-color: #fff; bottom: 0; content: \'\'; left: 0; position: absolute; right: 0; top: 0; transform: scaleX(0); transform-origin: 0 center; transition: transform .1s ease-in-out; } .letter::after { content: attr(data-letter); position: relative; } .letter--template { display: none; } .restart-button__container { margin-top: 2rem; text-align: center; } .restart-button { background-color: #fff; border: 0; border-radius: 6px; color: #333; cursor: pointer; font-family: inherit; font-size: 1rem; padding: .5rem 2rem; transition: background-color .15s ease-in-out; } .restart-button:hover, .restart-button:focus { background-color: #f15f79; color: #ffffff; } </style> </head> <body> <div class="container"> <div class="game-container" data-state="pre-game"> <h1 class="title"></h1> <div class="highscore-container"></div> <div class="letter__container"> <div class="letter letter--template" data-state="inactive"></div> </div> <div class="restart-button__container"> <button class="restart-button" type="button">再玩一次</button> </div> </div> </div> <script> const GameState = { PRE_GAME: \'pre-game\', RUNNING: \'running\', POST_GAME: \'post-game\' }; const LetterState = { ACTIVE: \'active\', INACTIVE: \'inactive\' }; class SpeedTest { constructor(gameContainer, letterContainer) { this.letters = []; this.remainingLetters = []; this.state = GameState.PRE_GAME; this.timer = null; this.gameContainer = gameContainer; this.letterContainer = letterContainer; this.highscore = Infinity; this.onKeyDown = this.onKeyDown.bind(this); this.restartGame = this.restartGame.bind(this); }; init(restart) { const availableLanguages = [\'en\', \'nb\', \'nn\', \'no\']; const preferredLanguage = SpeedTest.getPreferredLanguage(availableLanguages); this.letters = SpeedTest.getLetters(preferredLanguage); this.remainingLetters = this.letters; this.highscore = this.getHighscore(); if (this.highscore && this.highscore !== Infinity) { this.updateHighscoreText(this.highscore); } const letterTemplate = this.letterContainer.querySelector( \'.letter--template\' ); this.setTitle(\'按键盘上的A键启动,按空格键重新启动\'); this.renderLetters(letterTemplate); if (!restart) { this.addEventListeners(); this.getHighscore(); } }; static getPreferredLanguage(langs) { const defaultLang = \'en\'; const navgLangs = navigator.languages; const preferredLang = navgLangs.filter(lang => langs.indexOf(lang) > -1)[0]; return preferredLang || defaultLang; }; static getLetters(preferredLang) { let letters = []; switch (preferredLang) { case \'nb\': case \'nn\': case \'no\': letters = [ \'a\', \'b\', \'c\', \'d\', \'e\', \'f\', \'g\', \'h\', \'i\', \'j\', \'k\', \'l\', \'m\', \'n\', \'o\', \'p\', \'q\', \'r\', \'s\', \'t\', \'u\', \'v\', \'w\', \'x\', \'y\', \'z\', \'æ\', \'ø\', \'å\' ]; break; default: letters = [ \'a\', \'b\', \'c\', \'d\', \'e\', \'f\', \'g\', \'h\', \'i\', \'j\', \'k\', \'l\', \'m\', \'n\', \'o\', \'p\', \'q\', \'r\', \'s\', \'t\', \'u\', \'v\', \'w\', \'x\', \'y\', \'z\' ]; break; } return letters; }; renderLetters(template) { let letterElement; this.letters.forEach((letter, index) => { letterElement = template.cloneNode(true); letterElement.classList.remove(\'letter--template\'); letterElement.dataset.letter = letter; letterElement.dataset.index = index; this.letterContainer.appendChild(letterElement); }); // this.letterContainer.removeChild(template); }; addEventListeners() { document.addEventListener(\'keydown\', this.onKeyDown, false); const restartButton = this.gameContainer.querySelector(\'.restart-button\'); restartButton.addEventListener(\'click\', this.restartGame, false); }; onKeyDown(event) { if (event.key === \' \') { this.restartGame(); } else if (this.remainingLetters[0].toUpperCase() === event.key.toUpperCase()) { if (this.state === GameState.PRE_GAME) { this.startGame(); } this.updateLetter(this.remainingLetters[0], LetterState.ACTIVE); this.remainingLetters = this.remainingLetters.slice(1); if (this.remainingLetters.length === 0) { this.stopGame(); } else if (this.remainingLetters.length < this.letters.length / 5) { this.setTitle(\'只剩下几个了,继续!\'); } else if (this.remainingLetters.length < this.letters.length / 2) { this.setTitle(\'过半了!\'); } } else { const nextLetterElement = this.letterContainer.querySelector( `[data-state=${LetterState.INACTIVE}]` ); this.wrongLetter(nextLetterElement); } }; startGame() { this.gameContainer.dataset.state = GameState.RUNNING; this.state = GameState.RUNNING; this.setTitle(\'GO GO GO\'); this.startTimer(); }; stopGame() { const t2 = this.stopTimer(); const t1 = this.timer; this.gameContainer.dataset.state = GameState.POST_GAME; this.state = GameState.POST_GAME; let time = t2 - t1; time /= 1000; const timeString = this.formatTime(time); let newTitle = `您完成了! ✨ 时间是 ${timeString} 秒!`; if (this.highscore === null || time < this.highscore) { newTitle += \' 新纪录!\'; this.highscore = time; this.updateHighscoreText(time); this.setHighscore(time); } this.setTitle(newTitle); }; restartGame() { this.state = GameState.PRE_GAME; this.gameContainer.dataset.state = GameState.PRE_GAME; const letterElements = this.letterContainer.querySelectorAll(\'[data-letter]\'); for (let i = 0; i < letterElements.length; i++) { this.letterContainer.removeChild(letterElements[i]); } this.init(); }; startTimer() { this.timer = performance.now(); }; stopTimer() { return performance.now(); }; wrongLetter(element) { if (!(\'animate\' in element)) { return; } const styles = getComputedStyle(element); const backgroundColor = styles.getPropertyValue(\'--background-color\'); const errorRed = \'#f15f79\'; const blinkAnimation = [ { [\'--background-color\']: backgroundColor }, { [\'--background-color\']: errorRed }, { [\'--background-color\']: backgroundColor } ]; const blinkTiming = { duration: 100, iterations: 1 }; element.animate(blinkAnimation, blinkTiming); }; setTitle(text) { const title = this.gameContainer.querySelector(\'.title\'); title.innerText = text; }; updateLetter(letter, state) { const element = this.letterContainer.querySelector( `[data-letter=${letter}]` ); element.dataset.state = state; }; getHighscore() { let highscore = Infinity; if (\'localStorage\' in window) { highscore = window.localStorage.getItem(\'highscore\'); } if (highscore) { highscore = parseFloat(highscore); } else { highscore = Infinity; } return highscore; }; setHighscore(highscore) { if (\'localStorage\' in window) { window.localStorage.setItem(\'highscore\', highscore); } }; updateHighscoreText(newHighscore) { const highscoreContainer = this.gameContainer.querySelector( \'.highscore-container\' ); highscoreContainer.innerText = `个人最佳: ${this.formatTime(newHighscore)} 秒`; }; formatTime(time) { return time.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } } const gameContainer = document.querySelector(\'.game-container\'); const letterContainer = document.querySelector(\'.letter__container\'); const game = new SpeedTest(gameContainer, letterContainer); game.init(); </script> </body> </html>
版权声明:本文为xiewangfei123原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。