Last active
February 7, 2019 13:15
-
-
Save sempostma/fe2e45ca98f5d85a1e8bd2663d273fc9 to your computer and use it in GitHub Desktop.
final-approach.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<title>Game</title> | |
<style> | |
body { | |
margin: 0; | |
} | |
html { | |
overflow: hidden; | |
background: #000; | |
} | |
* { | |
font-family: monospace; | |
font-size: 16px; | |
} | |
canvas { | |
position: absolute; | |
} | |
#stack { | |
position: absolute; | |
left: 0; | |
top: 0; | |
color: #fff; | |
background: #001600; | |
border: 1px solid #003600; | |
padding: 3px; | |
border-radius: 4px; | |
} | |
#stack>div { | |
margin-bottom: 3px; | |
} | |
#stack>div:last-of-type { | |
margin-bottom: 0; | |
} | |
input { | |
background: #002000; | |
border: 1px solid #005500; | |
border-radius: 4px; | |
padding: 3px; | |
outline: none; | |
color: #fff; | |
} | |
button { | |
background: #002000; | |
border: 1px solid #005500; | |
border-radius: 4px; | |
padding: 3px; | |
outline: none; | |
margin-left: 2px; | |
color: #fff; | |
} | |
button:hover { | |
background: #003000; | |
} | |
button:active { | |
background: #005500; | |
} | |
input:hover { | |
background: #003000; | |
} | |
input:active { | |
background: #005500; | |
} | |
.callsign { | |
width: 80px; | |
margin-left: 5px; | |
display: inline-block; | |
vertical-align: middle; | |
vertical-align: baseline; | |
} | |
#mistakes { | |
color: #005500; | |
position: fixed; | |
right: 5px; | |
top: 2px; | |
font-size: 30px; | |
font-weight: bold; | |
} | |
fieldset { | |
appearance: none; | |
border: none; | |
background: transparent; | |
} | |
</style> | |
</head> | |
<body> | |
<canvas id="c"></canvas> | |
<fieldset> | |
<div id="stack"> | |
</div> | |
</fieldset> | |
<div id="mistakes"></div> | |
<script> | |
var stack = document.getElementById('stack'); | |
var mistakesElement = document.getElementById('mistakes'); | |
var canvas = document.getElementById('c'); | |
var ctx = canvas.getContext('2d'); | |
var globalSpeed = .005; | |
var spawnSpeed = .5; | |
var dotSpeed = .05; | |
var dotAmount = 20; | |
var airplanes = []; | |
var sepRadius = 50; | |
var sepRadiusSq = sepRadius * sepRadius; | |
var rm = []; | |
var airlines = ['KLM', 'DLT', 'JBU']; | |
var i = 0; | |
var j = 0; | |
var highlight = null; | |
var mistakes = 0; | |
resize(); | |
window.addEventListener('resize', resize); | |
window.addEventListener('orientationchange', resize); | |
document.addEventListener('click', unfocus); | |
document.addEventListener('mouseover', unfocus); | |
function unfocus() { | |
if (highlight) { | |
highlight = null; | |
requestAnimationFrame(render); | |
} | |
} | |
window.addEventListener('keydown', function (e) { | |
if (e.key === 'ArrowLeft') globalSpeed /= 1.3; | |
if (e.key === 'ArrowRight') globalSpeed *= 1.3; | |
}); | |
newPlane(); | |
setInterval(update, 1000 / 10); // 10 fps | |
function getRandHeight() { | |
var mid = canvas.height / 2; | |
return mid + (Math.random() - .5) * canvas.width; | |
} | |
function newPlane() { | |
var item = document.createElement('div'); | |
var speedRnd = 200 + Math.floor(Math.random() * 4) * 10; | |
var airplane = { | |
x: canvas.width, | |
y: getRandHeight(), | |
speed: speedRnd, | |
tgtSpeed: speedRnd, | |
callsign: airlines[Math.floor(Math.random() * airlines.length)] + Math.floor(Math.random() * 1000), | |
stack: item | |
}; | |
airplane.dots = [[airplane.x, airplane.y]]; | |
var callsign = document.createElement('span'); | |
callsign.appendChild(document.createTextNode(airplane.callsign)); | |
callsign.setAttribute('class', 'callsign'); | |
item.appendChild(callsign); | |
var input = document.createElement('input'); | |
input.value = airplane.tgtSpeed; | |
input.addEventListener('keydown', function (e) { | |
if (e.key === 'Enter') go(); | |
}); | |
input.addEventListener('focus', focus) | |
var btn = document.createElement('button'); | |
btn.tabIndex = -1; | |
btn.innerText = 'GO'; | |
btn.addEventListener('click', go); | |
item.addEventListener('mouseover', focus); | |
item.addEventListener('click', focus); | |
item.appendChild(input); | |
item.appendChild(btn); | |
stack.appendChild(item); | |
airplanes.push(airplane); | |
function focus(e) { | |
if (highlight !== airplane) { | |
highlight = airplane; | |
requestAnimationFrame(render); | |
} | |
e.stopPropagation(); | |
} | |
function go() { | |
airplane.tgtSpeed = Math.min(Math.max(input.value, 160), 250); | |
input.value = airplane.tgtSpeed; | |
} | |
} | |
function resize() { | |
canvas.width = window.innerWidth; | |
canvas.height = window.innerHeight; | |
} | |
function update() { | |
if ((i += globalSpeed) > spawnSpeed) { | |
i = 0; | |
newPlane(); | |
} | |
rm.splice(0, rm.length); | |
airplanes.forEach(function (airplane) { | |
airplane.speed -= Math.min(Math.max(airplane.speed - airplane.tgtSpeed, -globalSpeed * 100), globalSpeed * 100); | |
var deltaFromCenter = canvas.height / 2 - airplane.y; | |
var distFromCenter = deltaFromCenter / Math.abs(deltaFromCenter); | |
var yDelta = distFromCenter * Math.min(Math.abs(deltaFromCenter), globalSpeed * airplane.speed * 0.6); | |
if (Math.abs(deltaFromCenter) < 25) yDelta /= 1 + (25 - Math.abs(deltaFromCenter)) / 10; | |
var xDelta = airplane.speed * globalSpeed; | |
airplane.x -= xDelta - Math.abs(yDelta) * .5; | |
airplane.y += yDelta; | |
for (var i = 0; i < airplanes.length; i++) { | |
if (airplane === airplanes[i]) continue; | |
var x = Math.abs(airplanes[i].x - airplane.x); | |
var y = Math.abs(airplanes[i].y - airplane.y); | |
if ((x * x) + (y * y) < sepRadiusSq) { | |
mistakes += globalSpeed; | |
break; | |
} | |
} | |
if (airplane.x < 80) { | |
rm.push(airplane); | |
} | |
}); | |
if ((j += globalSpeed) > dotSpeed) { | |
j = 0; | |
airplanes.forEach(function (airplane) { | |
airplane.dots.unshift([airplane.x, airplane.y]); | |
if (airplane.dots.length > dotAmount) airplane.dots.pop(); | |
}); | |
} | |
rm.forEach(function (r) { | |
stack.removeChild(r.stack); | |
airplanes.splice(airplanes.indexOf(r), 1); | |
}); | |
requestAnimationFrame(render); | |
} | |
function render() { | |
ctx.fillStyle = '#000000'; | |
ctx.fillRect(0, 0, canvas.clientWidth, canvas.clientHeight); | |
ctx.fillStyle = '#ffffff'; | |
ctx.fillRect(50, canvas.clientHeight / 2, 40, 2); | |
ctx.fillText('09', 40, canvas.height / 2 - 2); | |
ctx.fillText('27', 80, canvas.height / 2 - 2); | |
ctx.fillStyle = ctx.strokeStyle = '#005500'; | |
mistakesElement.innerText = mistakes; | |
airplanes.forEach(function (airplane) { | |
for (var i = 0; i < airplanes.length; i++) { | |
if (airplane === airplanes[i]) continue; | |
var x = Math.abs(airplanes[i].x - airplane.x); | |
var y = Math.abs(airplanes[i].y - airplane.y); | |
if ((x * x) + (y * y) < sepRadiusSq) { | |
ctx.beginPath(); | |
ctx.arc(airplane.x, airplane.y, sepRadius, 0, Math.PI * 2, false); | |
ctx.fillStyle = 'rgba(255,0,0,0.2)'; | |
ctx.fill(); | |
break; | |
} | |
} | |
ctx.fillRect(airplane.x, airplane.y, 4, 4); | |
ctx.beginPath(); | |
ctx.moveTo(airplane.x, airplane.y); | |
if (highlight === airplane) { | |
ctx.font = "Bold 15px Arial"; | |
} else { | |
ctx.font = "15px Arial"; | |
} | |
ctx.lineTo(airplane.x - 50, airplane.y - 50, 150); | |
ctx.stroke(); | |
ctx.fillText(airplane.callsign, airplane.x - 80, airplane.y - 70); | |
ctx.fillText(Math.round(airplane.speed), airplane.x - 80, airplane.y - 55); | |
airplane.dots.forEach(function (dot) { | |
ctx.fillStyle = ctx.strokeStyle = '#005500'; | |
ctx.fillRect(dot[0], dot[1], 2, 2) | |
}); | |
}); | |
mistakesElement.innerText = Math.ceil(mistakes) + ' ERRORS'; | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment