Skip to main content

De volledige Code

Mocht je gaandeweg de weg zijn kwijtgeraakt, dan kun je hier de volledige code bekijken (en downloaden)

Volledige Code

ZIP

Volledige HTML code​

<html lang="en"><head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Stylesheet inladen -->
<link rel="stylesheet" href="assets/css/style.css">
<title>Whack-a-Mole</title>
</head>
<body>

<!--
De info box

-->
<div class="info">
<div class="box">
<div>
<h2>speed</h2>
<div class="speed">SLOW</div>
</div>
</div>
<div class="box">
<div>
<h2>score</h2>
<div class="score">0</div>
</div>
</div>
<div class="box">
<div>
<h2>time left</h2>
<div class="timer">47s</div>
</div>
</div>
</div>

<div class="content">
<div class="board">
<div class="hole"></div>
<div class="hole"></div>
<div class="hole"><img class="mole" src="./assets/img/mole.png"></div>
<div class="hole"></div>
<div class="hole"></div>
<div class="hole"></div>
<div class="hole"></div>
<div class="hole"></div>
<div class="hole"></div>
</div>
<div class="hammer active" style="top: 360px; left: 484px;"></div>
</div>

<script src="./js/main.js"></script>
<div class="logo"></div>

</body>
</html>

De CSS Code​

/* reset */
* {
margin: 0;
padding: 0;
}

/* Font */
@font-face {
font-family: 'edinterlock';
src: url('../fonts/ed-interlock.woff2') format('woff2'),
url('../fonts/ed-interlock.woff') format('woff');
font-weight: normal;
font-style: normal;

}
:root {
/* FONTS */
--headings: "edinterlock";

/* COLORS */
--dark: #1e1e1e;
--purple: #9e005d;
--white: #FFF;
--black: #000;
}

html, body {
height: 100%;
}

body {
background-color: var(--dark);
color: var(--white);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
overflow: hidden;
cursor: none;
}

/* Logo */
.logo {
position: fixed;
top: 20px;
left: 20px;
width: 500px;
height: 200px;
z-index: 10000;
background-image: url('../img/logo.png');
background-size: contain;
background-repeat: no-repeat;
background-position: top left;
}

/* Score, Speed & Timer boards */
.info {
display: flex;
gap: 20px;
margin: 40px;
}

.box {
display: flex;
height: 100px;
width: 200px;
align-items: center;
justify-content: center;
font-family: var(--headings);
}


.box h2 {
text-align: center;
margin-bottom: 10px;
color: var(--purple);
font-size: 2em;
}

.score {
font-size: 4em;
text-align: center;
}

.speed, .timer {
font-size: 3em;
text-align: center;
}
.speed {
text-transform: uppercase;
}

.board {
height: 400px;
width: 400px;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
gap: 20px;
}


/* Holes & Moles */
.hole {
background-color: var(--black);
border-radius: 50%;
position: relative;
overflow: hidden;
}

.hole .mole {
width: 70%;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 0;
animation: rise .3s ease-out;
}

/* Animation */
@keyframes rise {
0% { transform: translateX(-50%) translateY(100%);}
100% { transform: translateX(-50%) translateY(0);}
}


/* Hammer */
.hammer {
height: 110px;
width: 100px;
position: absolute;
bottom: 100px;
right: 100px;
background-image: url('../img/hammer.png');
background-size: 100% 100%;
transform: translate(-20%, -20%);
transition: transform .1s;
pointer-events: none;
}

.hammer.active{
transform: translate(-20%, -20%) rotate(-45deg);
}

JavaScript Code​

const hammer = document.querySelector('.hammer')
const holes = document.querySelectorAll('.hole')
const content = document.querySelector('.content')

const scoreBoard = document.querySelector('.score')
const speedBoard = document.querySelector('.speed')
const timerBoard = document.querySelector('.timer')

const SCORE_ADDITION = 50

/// Mole
const MOLE = './assets/img/mole.png'
const MOLE_WACKED = './assets/img/mole-whacked.png'

/// Speed
const SPEED_START = 1250
const SPEED_FASTER = 50
const SPEED_UPDATE = 100
const SPEED_MIN = 750

const SPEEDS = [
{ max: SPEED_START, txt: "SLOW" },
{ max: SPEED_START - SPEED_UPDATE, txt: "MEDIUM" },
{ max: SPEED_START - (2 * SPEED_UPDATE), txt: "FAST" },
{ max: SPEED_START - (4 * SPEED_UPDATE), txt: "RIDICULOUS"}
]

let score = 0
let speed = SPEED_START
let time = 60

/// Hamer
window.addEventListener('mousemove', e => {
hammer.style.top = e.pageY + 'px'
hammer.style.left = e.pageX + 'px'
})

window.addEventListener('mousedown', () => {
hammer.classList.add('active')
})

window.addEventListener('mouseup', () => {
hammer.classList.remove('active')
})

/// Speed
const getSpeedText = (speed) => {
let curSpeed = SPEEDS.filter( s => speed <= s.max)
return(curSpeed[curSpeed.length - 1].txt)
}

const setSpeed = () => {
if( score % SPEED_UPDATE === 0 && speed > SPEED_MIN) {
speed -= SPEED_FASTER
speedBoard.textContent = getSpeedText(speed)
}
}

/// Score
const setScore = () => {
score += SCORE_ADDITION
scoreBoard.textContent = score
setSpeed()
}

/// Countdown
const countDown = () => {
time--
timerBoard.textContent = `${ time }s`

if (time == 0) {
clearInterval(countDownTimerId)
content.remove()
}

}

/// Random Hole
const getRandomHole = () => {
return( Math.floor(Math.random() * holes.length) )
}

/// Mole
const createMole = () => {
const img = document.createElement('img')
img.classList.add('mole')
img.src = MOLE

img.addEventListener('click', () => {
setScore()
img.src = MOLE_WACKED
})

return(img)
}

/// Game Engine
const main = () => {
const hole = holes[getRandomHole()]
let timer = null
const img = createMole()
hole.appendChild(img)

timer = setTimeout(() => {
hole.removeChild(img)
main()
}, speed)
}

/// GO!
let countDownTimerId = setInterval(countDown, 1000)
main()