<!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>

 

posted on 2020-06-11 08:10  颉旺飞  阅读(372)  评论(0编辑  收藏  举报