Simon Game
Using CSS transformations and animations
by vishalvasani
HTML
<button>Start</button>
<div id="level">Simon level: <span>0</span></div>
<div id="pie">
<div id="1" class="piece"></div>
<div id="2" class="piece"></div>
<div id="3" class="piece"></div>
<div id="4" class="piece"></div>
</div>
<div id="record"></div>
CSS
* {
-webkit-user-select: none;
user-select: none;
}
body {
text-align: center;
background-color: antiquewhite;
}
body > * {
margin: 1vw 0;
color: #2b2b2b;
font-family: "Roboto Condensed";
}
button {
font-size: 4vw;
border-radius: 2vw;
border: 0.5vw solid #ccc;
padding: 1vw 2.5vw;
cursor: pointer;
text-transform: uppercase;
}
button:focus {
outline: none;
}
#level {
font-size: 3.8vw;
}
#record {
font-size: 2.8vw;
}
#pie {
width: 70vw;
height: 70vw;
margin: 2vw auto;
padding: 1.5vw;
background-color: #ddd;
border-radius: 50%;
/* Removing Gaps Between Inline child elements */
font-size: 0;
}
#pie.avoid-tap {
pointer-events: none;
}
/* Landscape (default is portrait) */
body.landscape > * {
margin: 1vh 0;
}
body.landscape #pie {
width: 70vh;
height: 70vh;
margin: 2vh auto;
padding: 1.5vh;
}
body.landscape button {
font-size: 4vh;
border-radius: 2vh;
border: 0.5vh solid #ccc;
padding: 1vh 2.5vh;
}
body.landscape #level {
font-size: 3.8vh;
}
body.landscape #record {
font-size: 2.8vh;
}
.piece {
width: 50%;
height: 50%;
display: inline-flex;
cursor: pointer;
}
button:active,
.piece:active {
transform: scale(0.9);
}
/* green */
.piece:nth-child(1) {
border-radius: 100% 0 0 0;
background-color: #9BC53D;
}
.piece:nth-child(1):active {
transform-origin: bottom right;
}
/* blue */
.piece:nth-child(2) {
border-radius: 0 100% 0 0;
background-color: #00A8E8;
}
.piece:nth-child(2):active {
transform-origin: bottom left;
}
/* orange */
.piece:nth-child(3) {
border-radius: 0 0 0 100%;
background-color: #FF9F1C;
}
.piece:nth-child(3):active {
transform-origin: top right;
}
/* red */
.piece:nth-child(4) {
border-radius: 0 0 100% 0;
background-color: #E71D36;
}
.piece:nth-child(4):active {
transform-origin: top left;
}
/* black */
.piece.fail {
background-color: #50514F;
content: "sdf";
}
.piece.blink {
animation: 1s blink ease-in-out...
JavaScript
const $button_ = document.querySelector('button');
const $level_ = document.querySelector('#level>span');
const $pie_ = document.querySelector('#pie');
const $$pieces_ = document.querySelectorAll('.piece');
const $record_ = document.querySelector('#record');
let pieces_ = [];
// No taps allow before start the game
$pie_.classList.add('avoid-tap');
/**
* Generate next piece (number)
* 1: green, 2: blue, 3: orange, 4: red
*/
function get_random_piece() { // generate a random number between 4 and 1
return Math.floor(Math.random() * (4)) + 1;
}
/**
* Show the sequence you should follow
*/
function show_sequence() {
let i = 0,
e;
$pie_.classList.add('avoid-tap');
(function blink() {
e = document.getElementById(pieces_[i]);
e.classList.remove('blink'); // remove class
void e.offsetWidth; // "restart" -> triggering reflow
e.classList.add('blink'); // show animation
i++;
if (i < pieces_.length) { // if there are more pieces
setTimeout(blink, 1000); // wait 1s before show next one
} else {
setTimeout(function() { // wait 1s before allow user to tap
$pie_.classList.remove('avoid-tap');
}, 1000);
}
})();
}
/**
* Check sequence
*/
let check_sequence = function() {
let i = 0;
return function(piece) {
if (typeof(piece) === 'undefined') { // reset
i = 0;
} else { // play
if (piece === pieces_[i]) { // success
i++;
// Keep playing
if (i >= pieces_.length) { // we have guessed all
$level_.textContent = pieces_.length; // set level before add
pieces_.push(get_random_piece()); // add one more
show_sequence();
i = 0; // check from the beggining
}
} else {
$level_.textContent = '☹ you failed';
document.getElementById(piece).classList.add('fail');
$pie_.classList.add('avoid-tap');
i = 0;
// Save user level if there is an improvement
if...