Add web-vanilla
This commit is contained in:
15
web-vanilla/index.html
Normal file
15
web-vanilla/index.html
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<title>JavaScript</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
|
||||||
|
|
||||||
|
<h1>HTML/JavaScript mini-projects</h1>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a href="reaction/reaction.html">Reaction test</a></li>
|
||||||
|
<li><a href="trill/trill.html">Trill test</a></li>
|
||||||
|
<li><a href="mania/mania.html">osu!mania playfield simulator</a></li>
|
||||||
|
</ul>
|
||||||
36
web-vanilla/mania/mania.html
Normal file
36
web-vanilla/mania/mania.html
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<title>osu!mania playfield</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
<script src="../shared.js"></script>
|
||||||
|
<script src="mania.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<canvas id="playfield" width="800" height="600" style="border: 1px solid #000000"></canvas>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<label for="columnstart">Column start</label><br>
|
||||||
|
<input type="range" id="columnstart" min="0" max="200" value="150">
|
||||||
|
<output id="columnstart_out"></output><br>
|
||||||
|
|
||||||
|
<label for="columnwidth">Column width</label><br>
|
||||||
|
<input type="range" id="columnwidth" min="16" max="70" value="40">
|
||||||
|
<output id="columnwidth_out"></output><br>
|
||||||
|
|
||||||
|
<label for="hitposition">Hit position</label><br>
|
||||||
|
<input type="range" id="hitposition" min="350" max="480" value="420">
|
||||||
|
<output id="hitposition_out"></output><br>
|
||||||
|
|
||||||
|
<label for="scrollspeed">Scroll speed</label><br>
|
||||||
|
<input type="range" id="scrollspeed" min="12" max="40" value="28">
|
||||||
|
<output id="scrollspeed_out"></output><br>
|
||||||
|
|
||||||
|
<label for="noteratio">Note ratio</label><br>
|
||||||
|
<input type="range" id="noteratio" min="0" max="1" value="0.5" step="0.1">
|
||||||
|
<output id="noteratio_out"></output><br>
|
||||||
|
|
||||||
|
<label for="keycount">Key count</label><br>
|
||||||
|
<input type="range" id="keycount" min="4" max="9" value="7">
|
||||||
|
<output id="keycount_out"></output><br>
|
||||||
112
web-vanilla/mania/mania.js
Normal file
112
web-vanilla/mania/mania.js
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
var canvas;
|
||||||
|
var ctx;
|
||||||
|
|
||||||
|
/* Base resolution in osu! skins used for scaling */
|
||||||
|
const internal_width = 640;
|
||||||
|
const internal_height = 480;
|
||||||
|
|
||||||
|
/* Updates per second */
|
||||||
|
const framerate = 60;
|
||||||
|
|
||||||
|
/* Colors for playfield */
|
||||||
|
const colors = {
|
||||||
|
playfield: "#000",
|
||||||
|
lane_separator: "#222",
|
||||||
|
hitposition: "#AA0",
|
||||||
|
note: "#FFF"
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Values retrieved from DOM */
|
||||||
|
var options = {
|
||||||
|
columnstart: 0,
|
||||||
|
columnwidth: 0,
|
||||||
|
hitposition: 0,
|
||||||
|
scrollspeed: 0,
|
||||||
|
noteratio: 0,
|
||||||
|
keycount: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clears the canvas */
|
||||||
|
function clear() {
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draws a rectangle */
|
||||||
|
function rect(x, y, w, h, color) {
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fillRect(x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draws a string */
|
||||||
|
function text(string, x, y, color) {
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fillText(string, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Translates osu! coordinates to screen coordinates */
|
||||||
|
function translate_pos(x, y) {
|
||||||
|
return {
|
||||||
|
x: Math.floor(x / internal_width * canvas.width),
|
||||||
|
y: Math.floor(y / internal_height * canvas.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
clear()
|
||||||
|
|
||||||
|
const columnstart = translate_pos(options.columnstart, 0);
|
||||||
|
const columnwidth = translate_pos(options.columnwidth, 0);
|
||||||
|
const hitposition = translate_pos(options.columnstart, options.hitposition);
|
||||||
|
|
||||||
|
const playfield_width = columnwidth.x * options.keycount;
|
||||||
|
const note_height = columnwidth.x * options.noteratio;
|
||||||
|
|
||||||
|
// playfield background
|
||||||
|
rect(columnstart.x, 0, playfield_width, canvas.height, colors.playfield);
|
||||||
|
|
||||||
|
// lane separators
|
||||||
|
for (var i = 0; i <= options.keycount; i++) {
|
||||||
|
rect(columnstart.x + i * columnwidth.x, 0, 1, canvas.height, colors.lane_separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// hitpos
|
||||||
|
rect(hitposition.x, hitposition.y, playfield_width, 5, colors.hitposition);
|
||||||
|
|
||||||
|
// notes
|
||||||
|
let y_pos = hitposition.y - note_height;
|
||||||
|
while (y_pos > 0) {
|
||||||
|
for (let i = 0; i < options.keycount; ++i) {
|
||||||
|
rect (
|
||||||
|
columnstart.x + i * columnwidth.x,
|
||||||
|
y_pos,
|
||||||
|
columnwidth.x, note_height, colors.note
|
||||||
|
);
|
||||||
|
|
||||||
|
// completely inaccurate but i cba to simulate the whole playfield
|
||||||
|
y_pos -= options.scrollspeed * 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function () {
|
||||||
|
create_prologue();
|
||||||
|
|
||||||
|
canvas = document.getElementById("playfield");
|
||||||
|
ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
ctx.font = "15px Arial";
|
||||||
|
ctx.textBaseline = "hanging";
|
||||||
|
|
||||||
|
for (let input of document.getElementsByTagName("input")) {
|
||||||
|
input.onchange = function () {
|
||||||
|
options[this.id] = this.value;
|
||||||
|
document.getElementById(this.id + "_out").innerText = this.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
input.onchange();
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(function () {
|
||||||
|
draw();
|
||||||
|
}, 1000 / framerate);
|
||||||
|
};
|
||||||
9
web-vanilla/reaction/area.css
Normal file
9
web-vanilla/reaction/area.css
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#area {
|
||||||
|
height: 400px;
|
||||||
|
background-color: #444;
|
||||||
|
color: #FFF;
|
||||||
|
font-size: 24px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
14
web-vanilla/reaction/reaction.html
Normal file
14
web-vanilla/reaction/reaction.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<title>Reaction time test</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
<link rel="stylesheet" href="area.css">
|
||||||
|
<script src="../shared.js"></script>
|
||||||
|
<script src="reaction.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="area"></div>
|
||||||
|
<h3 id="average"></h3>
|
||||||
|
<ul id="times"></ul>
|
||||||
88
web-vanilla/reaction/reaction.js
Normal file
88
web-vanilla/reaction/reaction.js
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
|
||||||
|
const states = {
|
||||||
|
START: 'start',
|
||||||
|
TIMEOUT: 'timeout',
|
||||||
|
READY: 'ready'
|
||||||
|
}
|
||||||
|
|
||||||
|
const colors = {
|
||||||
|
TIMEOUT: 'crimson',
|
||||||
|
START: 'dodgerblue',
|
||||||
|
READY: 'limegreen'
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = states.START;
|
||||||
|
var times = [];
|
||||||
|
|
||||||
|
var ready_time;
|
||||||
|
var ready_id; // timeout id
|
||||||
|
|
||||||
|
const min_time = 1000;
|
||||||
|
const max_time = 3000;
|
||||||
|
|
||||||
|
function handle_click() {
|
||||||
|
switch (state) {
|
||||||
|
// click received on start, activate timer and set state to timeout
|
||||||
|
case states.START: {
|
||||||
|
|
||||||
|
// this is a hack to access thisptr from setTimeout
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
ready_id = setTimeout(function () {
|
||||||
|
ready_time = Date.now();
|
||||||
|
|
||||||
|
self.style["background-color"] = colors.READY;
|
||||||
|
self.innerText = "Click now!";
|
||||||
|
|
||||||
|
state = states.READY;
|
||||||
|
}, min_time + (Math.random() * (max_time - min_time)));
|
||||||
|
|
||||||
|
this.style["background-color"] = colors.TIMEOUT;
|
||||||
|
this.innerText = "Get ready!";
|
||||||
|
|
||||||
|
state = states.TIMEOUT;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// click received during timeout, clear timeout and reset
|
||||||
|
case states.TIMEOUT: {
|
||||||
|
clearTimeout(ready_id);
|
||||||
|
|
||||||
|
this.style["background-color"] = colors.START;
|
||||||
|
this.innerText = "Too early!";
|
||||||
|
|
||||||
|
state = states.START;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// click received after timeout, calculate score and reset
|
||||||
|
case states.READY: {
|
||||||
|
let time = (Date.now() - ready_time);
|
||||||
|
times.push(time);
|
||||||
|
|
||||||
|
let average = times.reduce((a, b) => a + b) / times.length;
|
||||||
|
|
||||||
|
// update average text
|
||||||
|
document.getElementById("average").innerText = "Average: " + average + "ms";
|
||||||
|
|
||||||
|
// create and add time listing
|
||||||
|
var li = document.createElement("li");
|
||||||
|
li.appendChild(document.createTextNode(time + "ms"));
|
||||||
|
document.getElementById("times").appendChild(li);
|
||||||
|
|
||||||
|
this.style["background-color"] = colors.START;
|
||||||
|
this.innerText = time + "ms";
|
||||||
|
|
||||||
|
state = states.START;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function () {
|
||||||
|
create_prologue();
|
||||||
|
|
||||||
|
let area = document.getElementById("area");
|
||||||
|
area.innerText = "Click here to begin!";
|
||||||
|
area.onclick = handle_click;
|
||||||
|
}
|
||||||
13
web-vanilla/shared.js
Normal file
13
web-vanilla/shared.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
function create_prologue() {
|
||||||
|
let back_to_index = document.createElement("a");
|
||||||
|
back_to_index.setAttribute("href", "../index.html");
|
||||||
|
back_to_index.innerHTML = "Back to index";
|
||||||
|
|
||||||
|
let title = document.createElement("h1");
|
||||||
|
title.innerText = document.title;
|
||||||
|
|
||||||
|
let body = document.getElementsByTagName("body")[0];
|
||||||
|
body.insertBefore(title, body.firstChild);
|
||||||
|
body.insertBefore(back_to_index, body.firstChild);
|
||||||
|
}
|
||||||
13
web-vanilla/style.css
Normal file
13
web-vanilla/style.css
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
body {
|
||||||
|
color: #444;
|
||||||
|
font: 18px/1.6 Arial, Helvetica, sans-serif;
|
||||||
|
margin: 40px auto;
|
||||||
|
max-width: 650px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
line-height: 1.2
|
||||||
|
}
|
||||||
9
web-vanilla/trill/area.css
Normal file
9
web-vanilla/trill/area.css
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#area {
|
||||||
|
height: 400px;
|
||||||
|
background-color: #444;
|
||||||
|
color: #FFF;
|
||||||
|
font-size: 24px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
14
web-vanilla/trill/trill.html
Normal file
14
web-vanilla/trill/trill.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<title>Trill test</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
<link rel="stylesheet" href="area.css">
|
||||||
|
<script src="../shared.js"></script>
|
||||||
|
<script src="trill.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="area"></div>
|
||||||
|
<h3 id="average"></h3>
|
||||||
|
<ul id="times"></ul>
|
||||||
102
web-vanilla/trill/trill.js
Normal file
102
web-vanilla/trill/trill.js
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
|
||||||
|
const states = {
|
||||||
|
GETKEYS: 'getkeys',
|
||||||
|
TRILLING: 'trilling',
|
||||||
|
READY: 'ready',
|
||||||
|
RESULT: 'result'
|
||||||
|
}
|
||||||
|
|
||||||
|
const colors = {
|
||||||
|
GETKEYS: 'gray',
|
||||||
|
TRILLING: 'crimson',
|
||||||
|
READY: 'darkgray',
|
||||||
|
RESULT: 'dodgerblue'
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = states.GETKEYS;
|
||||||
|
var times = []
|
||||||
|
|
||||||
|
const max_count = 20;
|
||||||
|
|
||||||
|
var key1, key2;
|
||||||
|
var time_start;
|
||||||
|
var counter = 0;
|
||||||
|
|
||||||
|
function handle_press(e) {
|
||||||
|
switch (state) {
|
||||||
|
case states.GETKEYS: {
|
||||||
|
if (!key1) {
|
||||||
|
key1 = e.code;
|
||||||
|
|
||||||
|
area.style["background-color"] = colors.GETKEYS;
|
||||||
|
area.innerText = "Press 2nd key!";
|
||||||
|
} else {
|
||||||
|
key2 = e.code;
|
||||||
|
state = states.READY;
|
||||||
|
|
||||||
|
area.style["background-color"] = colors.READY;
|
||||||
|
area.innerText = "Test will start on first keypress.";
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case states.READY: {
|
||||||
|
if (e.code == key1 || e.code == key2) {
|
||||||
|
state = states.TRILLING;
|
||||||
|
|
||||||
|
time_start = Date.now();
|
||||||
|
|
||||||
|
area.style["background-color"] = colors.TRILLING;
|
||||||
|
handle_press(e); // update couter/text for first keypress
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case states.TRILLING: {
|
||||||
|
if (e.code == key1 || e.code == key2) {
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
area.innerText = counter + "/" + max_count;
|
||||||
|
|
||||||
|
if (counter == max_count) {
|
||||||
|
state = states.RESULT;
|
||||||
|
|
||||||
|
counter = 0;
|
||||||
|
|
||||||
|
let time = parseInt((max_count / ((Date.now() - time_start) / 1000) * 60 / 4));
|
||||||
|
times.push(time);
|
||||||
|
|
||||||
|
let average = times.reduce((a, b) => a + b) / times.length;
|
||||||
|
|
||||||
|
document.getElementById("average").innerText = "Average: " + average + "bpm";
|
||||||
|
|
||||||
|
var li = document.createElement("li");
|
||||||
|
li.appendChild(document.createTextNode(time + "bpm"));
|
||||||
|
document.getElementById("times").appendChild(li);
|
||||||
|
|
||||||
|
area.style["background-color"] = colors.RESULT;
|
||||||
|
area.innerText = time + "bpm" + '\n(press space to try again)';
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case states.RESULT: {
|
||||||
|
if (e.code == "Space") {
|
||||||
|
state = states.READY;
|
||||||
|
|
||||||
|
area.style["background-color"] = colors.READY;
|
||||||
|
area.innerText = "Test will start on first keypress.";
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function () {
|
||||||
|
create_prologue();
|
||||||
|
|
||||||
|
area = document.getElementById("area");
|
||||||
|
area.innerText = "Press 1st key!";
|
||||||
|
|
||||||
|
document.addEventListener('keydown', handle_press);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user