Vue.js typing game
HTML
<div id="app">
<div v-if="currentState === 'IDLE'" class="page idle-page">
<h1 class="title">타자연습</h1>
<button @click="start" class="ui-button">시작하기</button>
</div>
<div v-else-if="currentState === 'STATS'">
TODO: 작업해야함
</div>
<div v-else class="page game-page">
<div class="game-sentence">{{ currentSentence }}</div>
<div class="form-group">
<input
class="game-input"
type="text"
v-model="userInput"
@keyup.enter="handleInputEnter"
@keyup="handleInput"
autofocus
:disabled="currentState === 'CALCULATE'"
>
</div>
<div class="game-timer">{{ secondString }}</div>
</div>
</div>
CSS
html,
body,
#app,
.page {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
.ui-button {
font-size: 20px;
background-color: #fff;
color: #000;
border: 1px solid #000;
padding: 10px;
cursor: pointer;
}
.form-group {
box-sizing: border-box;
width: 90%;
padding: 10px;
}
.idle-page {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.game-page {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.game-sentence {
font-weight: bold;
font-size: 2rem;
user-select: none;
}
.game-input {
width: 100%;
padding: 10px;
height: 1em;
font-size: 2em;
}
.game-timer {
font-family: consolas;
}
JavaScript
const __init__ = ['ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'];
const __medial__ = ['ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ'];
const __final__ = ['', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'];
const seperate = function(str) {
var result = [];
for (var i = 0; i < str.length; i++) {
var _charCode = str.charCodeAt(i);
if (_charCode < 0xAC00 || _charCode > 0xD7A3) {
result.push(str.charAt(i));
continue;
}
_charCode -= 0xAC00;
var init_code, medi_code, fin_code;
fin_code = _charCode % 28;
medi_code = ((_charCode - fin_code) / 28) % 21;
init_code = (((_charCode - fin_code) / 28) - medi_code) / 21;
result.push(__init__[init_code]);
result.push(__medial__[medi_code]);
result.push(__final__[fin_code]);
}
return result;
}
const STATES = {
IDLE: 'IDLE',
READY: 'READY',
PLAYING: 'PLAYING',
CALCULATE: 'CALCULATE',
STATS: 'STATS'
}
new Vue({
el: "#app",
data() {
return {
currentState: STATES.IDLE,
currentSentence: 'hello',
userInput: '',
isStarted: false,
second: 0,
secondInterval: null,
amountOfMilliseconds: 33
}
},
computed: {
secondString() {
return `${this.second / 1000}초`
}
},
methods: {
handleInput(e) {
if (e.key === 'Enter') {
return
}
if (!this.isStarted) {
this.isStarted = true
this.currentState = STATES.PLAYING
this.secondInterval = setInterval(() => {
this.second += this.amountOfMilliseconds
}, this.amountOfMilliseconds)
}
},
handleInputEnter() {
this.validateSentence()
},
validateSentence() {
if (this.userInput === this.currentSentence) {
console.log('정답')
this.isStarted = false
...