diff --git a/web-vanilla/index.html b/web-vanilla/index.html new file mode 100644 index 0000000..af90b37 --- /dev/null +++ b/web-vanilla/index.html @@ -0,0 +1,15 @@ + + + +JavaScript + + + + +

HTML/JavaScript mini-projects

+ + \ No newline at end of file diff --git a/web-vanilla/mania/mania.html b/web-vanilla/mania/mania.html new file mode 100644 index 0000000..27b87cf --- /dev/null +++ b/web-vanilla/mania/mania.html @@ -0,0 +1,36 @@ + + + +osu!mania playfield + + + + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/web-vanilla/mania/mania.js b/web-vanilla/mania/mania.js new file mode 100644 index 0000000..2c37d6e --- /dev/null +++ b/web-vanilla/mania/mania.js @@ -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); +}; diff --git a/web-vanilla/reaction/area.css b/web-vanilla/reaction/area.css new file mode 100644 index 0000000..cf02916 --- /dev/null +++ b/web-vanilla/reaction/area.css @@ -0,0 +1,9 @@ +#area { + height: 400px; + background-color: #444; + color: #FFF; + font-size: 24px; + display: flex; + justify-content: center; + align-items: center; +} \ No newline at end of file diff --git a/web-vanilla/reaction/reaction.html b/web-vanilla/reaction/reaction.html new file mode 100644 index 0000000..8e47f5f --- /dev/null +++ b/web-vanilla/reaction/reaction.html @@ -0,0 +1,14 @@ + + + +Reaction time test + + + + + + + +
+

+ \ No newline at end of file diff --git a/web-vanilla/reaction/reaction.js b/web-vanilla/reaction/reaction.js new file mode 100644 index 0000000..3648df9 --- /dev/null +++ b/web-vanilla/reaction/reaction.js @@ -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; +} diff --git a/web-vanilla/shared.js b/web-vanilla/shared.js new file mode 100644 index 0000000..8c6ff64 --- /dev/null +++ b/web-vanilla/shared.js @@ -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); +} diff --git a/web-vanilla/style.css b/web-vanilla/style.css new file mode 100644 index 0000000..270eb97 --- /dev/null +++ b/web-vanilla/style.css @@ -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 +} diff --git a/web-vanilla/trill/area.css b/web-vanilla/trill/area.css new file mode 100644 index 0000000..cf02916 --- /dev/null +++ b/web-vanilla/trill/area.css @@ -0,0 +1,9 @@ +#area { + height: 400px; + background-color: #444; + color: #FFF; + font-size: 24px; + display: flex; + justify-content: center; + align-items: center; +} \ No newline at end of file diff --git a/web-vanilla/trill/trill.html b/web-vanilla/trill/trill.html new file mode 100644 index 0000000..424a55c --- /dev/null +++ b/web-vanilla/trill/trill.html @@ -0,0 +1,14 @@ + + + +Trill test + + + + + + + +
+

+ \ No newline at end of file diff --git a/web-vanilla/trill/trill.js b/web-vanilla/trill/trill.js new file mode 100644 index 0000000..e85b5e4 --- /dev/null +++ b/web-vanilla/trill/trill.js @@ -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); +}