Feat: initial proof of concept
This commit is contained in:
parent
1414e378aa
commit
608f6b9590
15 changed files with 184 additions and 0 deletions
28
index.css
Normal file
28
index.css
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
@keyframes blinker {
|
||||||
|
50% {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.timer {
|
||||||
|
font-size: 8em;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timer.over {
|
||||||
|
color: #0f0;
|
||||||
|
animation: blinker 2s ease infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > div {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
48
index.html
Normal file
48
index.html
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/crypto-js@4.1.1/crypto-js.js"></script>
|
||||||
|
|
||||||
|
<script src="js/index.js"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css" />
|
||||||
|
<link rel="stylesheet" href="index.css" />
|
||||||
|
|
||||||
|
<audio id="sound-0" src="sound/0.mp3" preload="auto"></audio>
|
||||||
|
<audio id="sound-1" src="sound/1.mp3" preload="auto"></audio>
|
||||||
|
<audio id="sound-2" src="sound/2.mp3" preload="auto"></audio>
|
||||||
|
<audio id="sound-3" src="sound/3.mp3" preload="auto"></audio>
|
||||||
|
<audio id="sound-4" src="sound/4.mp3" preload="auto"></audio>
|
||||||
|
<audio id="sound-5" src="sound/5.mp3" preload="auto"></audio>
|
||||||
|
<audio id="sound-6" src="sound/6.mp3" preload="auto"></audio>
|
||||||
|
<audio id="sound-7" src="sound/7.mp3" preload="auto"></audio>
|
||||||
|
<audio id="sound-8" src="sound/8.mp3" preload="auto"></audio>
|
||||||
|
<audio id="sound-9" src="sound/9.mp3" preload="auto"></audio>
|
||||||
|
<audio id="sound-ok-ready-go" src="sound/ok-ready-go.mp3" preload="auto"></audio>
|
||||||
|
<audio id="sound-silence" src="sound/silence.mp3" preload="auto"></audio>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<main class="container">
|
||||||
|
<div x-data>
|
||||||
|
<div @click="$store.localState.next()">
|
||||||
|
|
||||||
|
|
||||||
|
<hgroup>
|
||||||
|
<h1>OK! .. READY! ... GO!</h1>
|
||||||
|
<h2 x-text="$store.localState.stateHint"></h2>
|
||||||
|
</hgroup>
|
||||||
|
|
||||||
|
<div x-data="Timer">
|
||||||
|
<p :class="'timer' + (over ? ' over':'')"
|
||||||
|
x-text="time + 's'"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
108
js/index.js
Normal file
108
js/index.js
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
async function play() {
|
||||||
|
const number = document.getElementById("number").value;
|
||||||
|
sayNumber(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sayNumber(number) {
|
||||||
|
console.log(number)
|
||||||
|
number = number.toString();
|
||||||
|
number = number.replace(/[^0-9]/, "");
|
||||||
|
for(let i = 0; i < number.length; i++) {
|
||||||
|
await playAudio(document.getElementById(`sound-${number[i]}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function playAudio(audio){
|
||||||
|
return new Promise(res=>{
|
||||||
|
audio.play()
|
||||||
|
audio.onended = res
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Timer() {
|
||||||
|
return {
|
||||||
|
time: 0,
|
||||||
|
over: false,
|
||||||
|
init() {
|
||||||
|
setInterval(() => {
|
||||||
|
const startedAt = Alpine.store("localState").startedAt;
|
||||||
|
const resultTime = Alpine.store("localState").time;
|
||||||
|
|
||||||
|
let time;
|
||||||
|
if (!startedAt && !resultTime) {
|
||||||
|
time = 0;
|
||||||
|
this.over = false;
|
||||||
|
} else if (resultTime) {
|
||||||
|
time = (resultTime / 1000);
|
||||||
|
this.over = true;
|
||||||
|
} else {
|
||||||
|
this.over = false;
|
||||||
|
time =
|
||||||
|
((new Date().getTime() - startedAt) / 1000)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.time = time.toFixed(2)
|
||||||
|
}, 10);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("alpine:init", () => {
|
||||||
|
|
||||||
|
Alpine.store("localState", {
|
||||||
|
_state: 0,
|
||||||
|
// 0: idle
|
||||||
|
// 1: starting
|
||||||
|
// 2: running
|
||||||
|
// 3: stopped
|
||||||
|
|
||||||
|
startedAt: null,
|
||||||
|
time: null,
|
||||||
|
stateHint: "",
|
||||||
|
|
||||||
|
init() {
|
||||||
|
Alpine.effect(() => {
|
||||||
|
switch(this._state) {
|
||||||
|
case 0: {
|
||||||
|
this.startedAt = null;
|
||||||
|
this.time = null;
|
||||||
|
this.stateHint = "Tap to start";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
this.stateHint = "Get ready..."
|
||||||
|
playAudio(document.getElementById("sound-ok-ready-go")).then(() => {
|
||||||
|
Alpine.store("localState")._state = 2;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
this.stateHint = "Tap to stop"
|
||||||
|
this.startedAt = new Date().getTime() - 200;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this.stateHint = "Tap to reset"
|
||||||
|
this.time = new Date().getTime() - this.startedAt;
|
||||||
|
sayNumber((this.time / 1000).toFixed(2));
|
||||||
|
this.startedAt = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
next() {
|
||||||
|
console.log("next");
|
||||||
|
playAudio(document.getElementById("sound-silence"))
|
||||||
|
|
||||||
|
if(this._state == 1) return;
|
||||||
|
|
||||||
|
this._state = (this._state + 1) % 4
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
})
|
BIN
sound/0.mp3
Normal file
BIN
sound/0.mp3
Normal file
Binary file not shown.
BIN
sound/1.mp3
Normal file
BIN
sound/1.mp3
Normal file
Binary file not shown.
BIN
sound/2.mp3
Normal file
BIN
sound/2.mp3
Normal file
Binary file not shown.
BIN
sound/3.mp3
Normal file
BIN
sound/3.mp3
Normal file
Binary file not shown.
BIN
sound/4.mp3
Normal file
BIN
sound/4.mp3
Normal file
Binary file not shown.
BIN
sound/5.mp3
Normal file
BIN
sound/5.mp3
Normal file
Binary file not shown.
BIN
sound/6.mp3
Normal file
BIN
sound/6.mp3
Normal file
Binary file not shown.
BIN
sound/7.mp3
Normal file
BIN
sound/7.mp3
Normal file
Binary file not shown.
BIN
sound/8.mp3
Normal file
BIN
sound/8.mp3
Normal file
Binary file not shown.
BIN
sound/9.mp3
Normal file
BIN
sound/9.mp3
Normal file
Binary file not shown.
BIN
sound/ok-ready-go.mp3
Normal file
BIN
sound/ok-ready-go.mp3
Normal file
Binary file not shown.
BIN
sound/silence.mp3
Normal file
BIN
sound/silence.mp3
Normal file
Binary file not shown.
Loading…
Reference in a new issue