463 lines
9.0 KiB
Vue
463 lines
9.0 KiB
Vue
<template>
|
|
<div class="simeon">
|
|
<div class="simeon-wrapper">
|
|
<div class="overlay">
|
|
<div class="start">
|
|
<div class="score">
|
|
{{score}}
|
|
</div>
|
|
|
|
<button :disabled="started" @click="startGame()">
|
|
START
|
|
</button>
|
|
</div>
|
|
|
|
<div class="reset">
|
|
<button @click="reset()">
|
|
RESET
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="simeon-game-board">
|
|
<div :key="space"
|
|
@click="checkInput(i)"
|
|
v-for="(space,i) in spaces"
|
|
:class="[space.klass, {fail: fail, success: success}] "
|
|
class="simeon-game-spaces">
|
|
|
|
<div class="lite" :class="{blink: blink == i}">
|
|
<div class="key">
|
|
{{space.key}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
|
|
|
|
const getAudioContext = () => {
|
|
let ctx = null, usingWebAudio = true;
|
|
|
|
try {
|
|
// Fix up for prefixing
|
|
window.AudioContext = window.AudioContext || window.webkitAudioContext;
|
|
ctx = new AudioContext();
|
|
|
|
// context state at this time is `undefined` in iOS8 Safari
|
|
if (usingWebAudio && ctx.state === 'suspended') {
|
|
var resume = function () {
|
|
ctx.resume();
|
|
|
|
setTimeout(function () {
|
|
if (ctx.state === 'running') {
|
|
document.body.removeEventListener('touchend', resume, false);
|
|
}
|
|
}, 0);
|
|
};
|
|
document.body.addEventListener('touchend', resume, false);
|
|
}
|
|
|
|
return ctx
|
|
|
|
} catch (e) {
|
|
usingWebAudio = false;
|
|
alert('Sorry, Web Audio API is not supported in this browser');
|
|
return null;
|
|
}
|
|
};
|
|
|
|
export default {
|
|
name: 'App',
|
|
components: {
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
started: false,
|
|
audioContext: null,
|
|
oscMain: null,
|
|
gainMain: null,
|
|
volume: 60,
|
|
count: 4,
|
|
score: 0,
|
|
blink: null,
|
|
check: [],
|
|
pattern: [],
|
|
running: false,
|
|
fail: false,
|
|
success: false,
|
|
playing: false,
|
|
spaces: [
|
|
{
|
|
key: "Q",
|
|
tone: 196.00,
|
|
klass: 'piece-t-l'
|
|
},
|
|
{
|
|
key: "W",
|
|
tone: 220.00,
|
|
klass: 'piece-t-r'
|
|
},
|
|
{
|
|
key: "A",
|
|
tone: 261.63,
|
|
klass: 'piece-b-l'
|
|
},
|
|
{
|
|
key: "S",
|
|
tone: 293.66,
|
|
klass: 'piece-b-r'
|
|
},
|
|
]
|
|
}
|
|
},
|
|
|
|
created() {
|
|
window.addEventListener('keyup', (e) => {
|
|
if (e.key == 'q') {
|
|
this.playOSC(this.spaces[0].tone)
|
|
this.checkInput(0)
|
|
}
|
|
})
|
|
|
|
window.addEventListener('keyup', (e) => {
|
|
if (e.key == 'w') {
|
|
this.playOSC(this.spaces[1].tone)
|
|
this.checkInput(1)
|
|
}
|
|
})
|
|
|
|
window.addEventListener('keyup', (e) => {
|
|
if (e.key == 'a') {
|
|
this.playOSC(this.spaces[2].tone)
|
|
this.checkInput(2)
|
|
}
|
|
})
|
|
|
|
window.addEventListener('keyup', (e) => {
|
|
if (e.key == 's') {
|
|
this.playOSC(this.spaces[3].tone)
|
|
this.checkInput(3)
|
|
}
|
|
})
|
|
},
|
|
|
|
methods: {
|
|
playOSC: function (freq = 440.0, type = 'sine') {
|
|
if (this.oscMain != null) {
|
|
console.log("stopping")
|
|
this.oscMain.stop();
|
|
this.oscMain = null;
|
|
}
|
|
|
|
this.oscMain = null;
|
|
this.oscMain = this.audioContext.createOscillator();
|
|
|
|
this.oscMain.type = type;
|
|
this.oscMain.frequency.value = freq;
|
|
|
|
this.gainMain = this.gainMain || this.audioContext.createGain();
|
|
this.gainMain.gain.value = this.volume / 100.0;
|
|
|
|
this.oscMain.connect(this.gainMain);
|
|
this.gainMain.connect(this.audioContext.destination);
|
|
|
|
this.oscMain.start();
|
|
this.oscMain.stop(this.audioContext.currentTime + 0.2);
|
|
},
|
|
|
|
checkInput(i) {
|
|
if(!this.playing) {
|
|
if(this.check.length < this.pattern.length) {
|
|
this.playOSC(this.spaces[i].tone)
|
|
this.check.push(i)
|
|
}
|
|
|
|
for(let i = 0; i < this.check.length; i++) {
|
|
if(this.check[i] != this.pattern[i]) {
|
|
this.fail = true
|
|
}
|
|
}
|
|
|
|
if(!this.fail && (this.check.length == this.pattern.length)) {
|
|
this.score += 1
|
|
this.check = []
|
|
this.success = true
|
|
|
|
setTimeout(()=>{
|
|
|
|
setTimeout(()=>{
|
|
this.success = false
|
|
this.fail = false
|
|
},200)
|
|
|
|
this.check = []
|
|
this.addToPattern()
|
|
this.playPattern()
|
|
},200)
|
|
}
|
|
}
|
|
},
|
|
|
|
startGame() {
|
|
this.started = true
|
|
if(!this.audioContext) {
|
|
this.audioContext = getAudioContext()
|
|
}
|
|
|
|
this.playOSC(this.spaces[0].tone)
|
|
|
|
this.reset()
|
|
},
|
|
|
|
reset() {
|
|
this.fail = false
|
|
this.success = false
|
|
this.pattern = []
|
|
this.check = []
|
|
this.score = 0
|
|
//this.generatePattern()
|
|
this.addToPattern()
|
|
this.playPattern()
|
|
},
|
|
|
|
playPattern() {
|
|
this.playing = true
|
|
for(let i = 0; i < this.pattern.length; i++ ) {
|
|
setTimeout(()=> {
|
|
setTimeout(()=> {
|
|
this.blink = this.pattern[i]
|
|
this.playOSC(this.spaces[this.pattern[i]].tone)
|
|
}, 900)
|
|
|
|
setTimeout(()=> {
|
|
this.blink = null
|
|
if(i == this.pattern.length - 1) {
|
|
this.playing = false
|
|
}
|
|
}, 1000)
|
|
}, 600 * (i+1))
|
|
}
|
|
},
|
|
|
|
addToPattern() {
|
|
this.pattern.push(Math.floor(Math.random() * 4))
|
|
},
|
|
|
|
generatePattern() {
|
|
let pattern = []
|
|
|
|
for(let i = 0; i < this.count; i++ ) {
|
|
pattern[i] = Math.floor(Math.random() * 4)
|
|
setTimeout(()=> {
|
|
setTimeout(()=> {
|
|
this.blink = pattern[i]
|
|
|
|
}, 600)
|
|
|
|
setTimeout(()=> {
|
|
this.blink = null
|
|
}, 800)
|
|
}, 600 * (i+1))
|
|
}
|
|
|
|
this.pattern = pattern
|
|
|
|
return pattern
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
html, body{
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: 0;
|
|
margin: 0;
|
|
background: black;
|
|
}
|
|
#app {
|
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
text-align: center;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.simeon-wrapper {
|
|
margin: 0;
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
-moz-transform: translateX(-50%) translateY(-50%);
|
|
-webkit-transform: translateX(-50%) translateY(-50%);
|
|
transform: translateX(-50%) translateY(-50%);
|
|
display: flex;
|
|
align-items: center;
|
|
vertical-align: middle;
|
|
justify-content: center;
|
|
margin: auto;
|
|
width: 100%;
|
|
max-width: 640px;
|
|
height: 100vw;
|
|
max-height: 640px;
|
|
border: 3px dotted black;
|
|
}
|
|
|
|
|
|
.simeon-game-board {
|
|
display: grid;
|
|
grid-template-columns: 50% 50%;
|
|
width: 75%;
|
|
height: 75%;
|
|
}
|
|
|
|
.overlay {
|
|
background: #585858;
|
|
border-radius: 100%;
|
|
width: 30%;
|
|
height: 30%;
|
|
margin: 0;
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
border: 16px solid black;
|
|
transform: translateX(-50%) translateY(-50%);
|
|
}
|
|
|
|
.score {
|
|
background: black;
|
|
border-radius: 6px;
|
|
color: #fd0010;
|
|
width: 50%;
|
|
height: 32px;
|
|
font-size: 18px;
|
|
font-family: Courier New;
|
|
line-height: 32px;
|
|
margin: 0;
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translateX(-50%) translateY(-50%);
|
|
}
|
|
|
|
.simeon-game-spaces {
|
|
box-sizing: border-box;
|
|
align-content: center;
|
|
justify-content: center;
|
|
vertical-align: middle;
|
|
display: grid;
|
|
width: 100%;
|
|
height: 100%;
|
|
border: 8px solid black;
|
|
box-shadow: inset -12px -9px 20px 2px #000000a6; /*inset 9px 4px 6px 2px white*/
|
|
|
|
}
|
|
|
|
.piece-t-l {
|
|
background: #12cb56;
|
|
border-top-left-radius: 100%;
|
|
}
|
|
.piece-t-r {
|
|
background: #fd0010;
|
|
border-top-right-radius: 100%;
|
|
}
|
|
.piece-b-l {
|
|
background: #f1c200;
|
|
border-bottom-left-radius: 100%;
|
|
}
|
|
.piece-b-r {
|
|
background: #1264ca;
|
|
border-bottom-right-radius: 100%;
|
|
}
|
|
|
|
.lite {
|
|
font-size: 25px;
|
|
font-weight: bold;
|
|
line-height: 50px;
|
|
color: white;
|
|
box-sizing: border-box;
|
|
border-radius: 100%;
|
|
height: 50px;;
|
|
width: 50px;
|
|
}
|
|
|
|
.blink {
|
|
background: #ffffff7d;
|
|
box-shadow: 0px 0px 22px 12px white;
|
|
}
|
|
|
|
.start, .reset {
|
|
height: 50%;
|
|
width: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.start button, .reset button {
|
|
outline: none;
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
height: 100%;
|
|
background: darkslategray;
|
|
text-shadow: 0px 0px 3px white;
|
|
color: black;
|
|
width: 100%;
|
|
}
|
|
|
|
.start button {
|
|
border-radius: 100px 100px 0 0;
|
|
}
|
|
|
|
.reset button {
|
|
border-radius: 0 0 100px 100px;
|
|
}
|
|
|
|
.fail {
|
|
background: #fd0010;
|
|
}
|
|
|
|
.success {
|
|
/*background: #12cb56;*/
|
|
background: White;
|
|
}
|
|
|
|
.level {
|
|
font-size: 32px;
|
|
font-family: Courier;
|
|
font-weight: bold;
|
|
width: 100%;
|
|
color: #f1c200;
|
|
box-sizing: border-box;
|
|
text-align: center;
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
padding: 16px;
|
|
}
|
|
|
|
@media (max-width:640px) {
|
|
.simeon-wrapper {
|
|
max-width: 100%;
|
|
}
|
|
|
|
.simeon-game-board {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
.start button, .reset button {
|
|
font-size: 14px;
|
|
}
|
|
.key {
|
|
display: none;
|
|
}
|
|
}
|
|
</style>
|