Skip to content

Commit

Permalink
type functionality added.
Browse files Browse the repository at this point in the history
  • Loading branch information
riyaahlawat committed Jun 3, 2024
1 parent bcf49fc commit e4612c1
Show file tree
Hide file tree
Showing 3 changed files with 316 additions and 1 deletion.
18 changes: 17 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,25 @@
<link rel="stylesheet" href="./styles-type.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200" />
<link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet">
</head>
<body>
<!-- .....start your work..... -->
<main>
<div id="header">
<div id="info"></div>
<div id="buttons">
<button id="newGameBtn">New game</button>
</div>
</div>
<div id="game" tabindex="0">
<div id="words"></div>
<div id="cursor"></div>
<div id="focus-error">
<span class="material-symbols-outlined">highlight_mouse_cursor</span>
Click here to focus.</div>
</div>
</main>
<script src="typing.js"></script>
</body>
</html>
116 changes: 116 additions & 0 deletions styles-type.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
--bgColor: rgb(253, 246, 227);
--textColor: #D91C81;
--black: #3A3335;
--incorrect: rgb(40, 207, 34);
}

body {
Expand All @@ -11,3 +12,118 @@ body {
font-family: "Poppins", sans-serif;
color: var(--textColor);
}

main{
width: 80%;
margin: 50px auto;
font-size: 1.4rem;
}

#header{
display: grid;
grid-template-columns: 1fr 1fr;
margin: 20px 6px 30px;
}

#buttons{
text-align: right;
}

#info{
color: var(--textColor);
}

button{
background: var(--bgColor);
border: 1px solid var(--black);
color: var(--textColor);
padding: 5px 20px;
border-radius: 5px;
cursor: pointer;
}

div#game{
line-height: 35px;
height: 105px;
overflow: hidden;
position: relative;
}

div#game:focus{
outline:0;
}

#words{
filter: blur(5px);
color: var(--textColor);
}

#game:focus #words{
filter: blur(0);
}

#focus-error {
color: var(--black);
position: absolute;
inset: 0;
text-align: center;
padding-top: 35px;
}

#game:focus #focus-error{
display:none;
}

div.word{
/* from formatWord() in js file */
/* each word a div */
display: inline-block;
margin: 0 5px;
}

.letter.correct{
color: var(--black);
}
.letter.incorrect{
color: var(--incorrect);
}

@keyframes blink{
0%{
opacity: 1;
}
50%{
opacity: 0;
}
100%{
opacity: 1;
}
}

#cursor{
display:none;
width: 2px;
height: 1.6rem;
background: var(--black);
position: fixed;
top: 119px;
left: 58px;
animation: blink .3s infinite;
}

#game:focus #cursor{
display: block;
}

#game.over #words{
opacity: .5;
filter: blur(0px);
}

#game.over:focus #cursor{
display: none;
}

#game.over #focus-error {
display: none;
}
183 changes: 183 additions & 0 deletions typing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
const words = 'Amazon Web Services (AWS) offers a comprehensive suite of cloud computing services, enabling businesses to build and deploy scalable, resilient, and secure applications. With AWS, organizations can access a wide range of services including compute, storage, databases, machine learning, analytics, and more. Services like Amazon EC2 provide resizable compute capacity in the cloud, while Amazon S3 offers highly durable object storage. Managed database services like Amazon RDS and Amazon DynamoDB simplify database management tasks, and AWS Lambda allows developers to run code without provisioning or managing servers. With AWS, businesses can innovate faster, reduce infrastructure costs, and scale their applications globally with ease.'.split(' ');
const wordsCount = words.length;
const gameTime = 30 * 1000; // milliseconds
window.timer = null;
window.gameStart = null;
window.pauseTime = 0;

function addClass(el,name) {
el.className += ' '+name;
}

function removeClass(el,name) {
el.className = el.className.replace(name,'');
}

function randomWord() {
const randomIndex = Math.ceil(Math.random() * wordsCount);
return words[randomIndex - 1];
}

function formatWord(word) {
// each word a div and each letter diff class
return `<div class="word"><span class="letter">${word.split('').join('</span><span class="letter">')}</span></div>`;
}

function newGame() {
document.getElementById('words').innerHTML = '';
for (let i = 0; i < 200; i++) {
document.getElementById('words').innerHTML += formatWord(randomWord());
}

addClass(document.querySelector('.word'), 'current');
addClass(document.querySelector('.letter'), 'current');

document.getElementById('info').innerHTML = (gameTime / 1000) + '';
window.timer = null;
}

function getWpm() {
const words = [...document.querySelectorAll('.word')];
const lastTypedWord = document.querySelector('.word.current');
const lastTypedWordIndex = words.indexOf(lastTypedWord) + 1;
const typedWords = words.slice(0, lastTypedWordIndex);
const correctWords = typedWords.filter(word => {
const letters = [...word.children];
const incorrectLetters = letters.filter(letter => letter.className.includes('incorrect'));
const correctLetters = letters.filter(letter => letter.className.includes('correct'));
return incorrectLetters.length === 0 && correctLetters.length === letters.length;
});
return correctWords.length / gameTime * 60000;
}

function gameOver() {
clearInterval(window.timer);
addClass(document.getElementById('game'), 'over');
const result = getWpm();
document.getElementById('info').innerHTML = `WPM: ${result}`;
}

document.getElementById('game').addEventListener('keyup', ev => {
const key = ev.key;
const currentWord = document.querySelector('.word.current');
const currentLetter = document.querySelector('.letter.current');

// What user has typed.
const expected = currentLetter?.innerHTML || ' ';
const isLetter = key.length === 1 && key !== ' ';
const isSpace = key === ' ';
const isBackspace = key === 'Backspace';
const isFirstLetter = currentLetter === currentWord.firstChild;
const lastChild = currentWord.lastChild;


if (document.querySelector('#game.over')) {
return;
}

console.log({key,expected});

if (!window.timer && (isLetter || isSpace)) {
window.timer = setInterval(() => {
if (!window.gameStart) {
window.gameStart = (new Date()).getTime();
}
const currentTime = (new Date()).getTime();
const msPassed = currentTime - window.gameStart;
const sPassed = Math.round(msPassed / 1000);
const sLeft = Math.round((gameTime / 1000) - sPassed);
if (sLeft <= 0) {
gameOver();
return;
}
document.getElementById('info').innerHTML = sLeft + '';
}, 1000);
}


// When user has clicked a letter.
if (isLetter) {
if (currentLetter) {
addClass(currentLetter, key === expected ? 'correct' : 'incorrect');
removeClass(currentLetter, 'current');
if (currentLetter.nextSibling) {
addClass(currentLetter.nextSibling, 'current');
}
} else { // key pressed on empty space
const incorrectLetter = document.createElement('span');
incorrectLetter.innerHTML = key;
incorrectLetter.className = 'letter incorrect extra';
currentWord.appendChild(incorrectLetter);
}
}

// When user has clicked a space.
if (isSpace) {
if (expected !== ' ') {
const lettersToInvalidate = [...document.querySelectorAll('.word.current .letter:not(.correct)')];
lettersToInvalidate.forEach(letter => {
addClass(letter, 'incorrect');
});
}
removeClass(currentWord, 'current');
addClass(currentWord.nextSibling, 'current');
if (currentLetter) {
removeClass(currentLetter, 'current');
}
addClass(currentWord.nextSibling.firstChild, 'current');
}

if (isBackspace) {
if (currentLetter && isFirstLetter) {
// make prev word current, last letter current
removeClass(currentWord, 'current');
addClass(currentWord.previousSibling, 'current');
removeClass(currentLetter, 'current');
if(!currentWord.previousSibling.lastChild.classList.contains('extra')) {
addClass(currentWord.previousSibling.lastChild, 'current');
removeClass(currentWord.previousSibling.lastChild, 'incorrect');
removeClass(currentWord.previousSibling.lastChild, 'correct');
} else {
currentWord.previousSibling.lastChild.remove();
}

}
if (currentLetter && !isFirstLetter) {
// move back one letter, invalidate letter
removeClass(currentLetter, 'current');
addClass(currentLetter.previousSibling, 'current');
removeClass(currentLetter.previousSibling, 'incorrect');
removeClass(currentLetter.previousSibling, 'correct');
}
if (!currentLetter && !lastChild.classList.contains('extra')) {
addClass(currentWord.lastChild, 'current');
removeClass(currentWord.lastChild, 'incorrect');
removeClass(currentWord.lastChild, 'correct');
}
if(!currentLetter && lastChild.classList.contains('extra')) {
lastChild.remove();
}
}

// move lines / words
if (currentWord.getBoundingClientRect().top > 150) {
const words = document.getElementById('words');
const margin = parseInt(words.style.marginTop || '0px');
words.style.marginTop = (margin - 35) + 'px';
}

// move cursor
const nextLetter = document.querySelector('.letter.current');
const nextWord = document.querySelector('.word.current');
const cursor = document.getElementById('cursor');
cursor.style.top = (nextLetter || nextWord).getBoundingClientRect().top + 2 + 'px';
cursor.style.left = (nextLetter || nextWord).getBoundingClientRect()[nextLetter ? 'left' : 'right'] + 'px';
});

document.getElementById('newGameBtn').addEventListener('click', () => {
gameOver();
newGame();
location.reload();
});

newGame();

0 comments on commit e4612c1

Please sign in to comment.