Add initial codes.
This commit is contained in:
commit
b2ba582cec
|
|
@ -0,0 +1,12 @@
|
||||||
|
# Number Slide
|
||||||
|
|
||||||
|
Simple number slide game using HTML and vanila JavaScript.
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="screenshot.png"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
- Open `index.html` on a modern browser.
|
||||||
|
- Edit the files on your favorite text editor.
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
background-color: rgb(240, 240, 244);
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
background-color: rgb(230, 230, 234);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5em;
|
||||||
|
color: rgb(57, 57, 61);
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgb(110, 110, 114);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer a {
|
||||||
|
color: rgb(110, 110, 114);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrapper {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 500px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 2em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls {
|
||||||
|
display: flex;
|
||||||
|
margin: 2em 0 1em 0;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls button {
|
||||||
|
background-color: rgb(220, 220, 224);
|
||||||
|
border: 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0.7em 1.5em;
|
||||||
|
transition: all 0.12s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls button:not([disabled]):hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #04bbd9;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#stage {
|
||||||
|
margin: 4em auto 6em auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#stage section {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 130px;
|
||||||
|
border-radius: 8px;
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: [c1] 33.33% [c2] 33.33% [c3] 33.33% [c-end];
|
||||||
|
grid-template-rows: [r1] 33.33% [r2] 33.33% [r3] 33.33% [r-end];
|
||||||
|
filter: drop-shadow(0 0 8px rgba(0, 0, 0, 0.15));
|
||||||
|
}
|
||||||
|
|
||||||
|
#stage button {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 3em;
|
||||||
|
background: rgb(250, 250, 252);
|
||||||
|
color: rgb(50, 50, 55);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: transform 0.12s ease-out, background-color 0.12s ease-out;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid rgb(198, 198, 201);
|
||||||
|
grid-column-start: c1;
|
||||||
|
grid-row-start: r1;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#stage button.fast {
|
||||||
|
transition: transform 0.08s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#stage.done button[disabled] {
|
||||||
|
background-color: #20a076;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c1.r1 { transform: translate(0, 0); }
|
||||||
|
.c1.r2 { transform: translate(0, 100%); }
|
||||||
|
.c1.r3 { transform: translate(0, 200%); }
|
||||||
|
|
||||||
|
.c2.r1 { transform: translate(100%, 0); }
|
||||||
|
.c2.r2 { transform: translate(100%, 100%); }
|
||||||
|
.c2.r3 { transform: translate(100%, 200%); }
|
||||||
|
|
||||||
|
.c3.r1 { transform: translate(200%, 0); }
|
||||||
|
.c3.r2 { transform: translate(200%, 100%); }
|
||||||
|
.c3.r3 { transform: translate(200%, 200%); }
|
||||||
|
|
@ -0,0 +1,249 @@
|
||||||
|
var grid = [1, 2, 3, 4, 5, 6, 7, 8, 0 ];
|
||||||
|
var COLS = 3;
|
||||||
|
var ROWS = grid.length / COLS;
|
||||||
|
var SHUFFLES = 35;
|
||||||
|
|
||||||
|
var MOV_LEFT = 0;
|
||||||
|
var MOV_RIGHT = 1;
|
||||||
|
var MOV_UP = 2;
|
||||||
|
var MOV_DOWN = 3;
|
||||||
|
|
||||||
|
var tiles = document.querySelectorAll('#stage button');
|
||||||
|
var textTime = document.getElementById('time');
|
||||||
|
var buttonShuffle = document.getElementById('shuffle');
|
||||||
|
var stage = document.getElementById('stage');
|
||||||
|
var stageSection = document.querySelector('#stage section');
|
||||||
|
var wrapper = document.getElementById('wrapper');
|
||||||
|
|
||||||
|
var timer = null;
|
||||||
|
var startTime = 0;
|
||||||
|
|
||||||
|
function getRow(index) {
|
||||||
|
return Math.floor(index / COLS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCol(index) {
|
||||||
|
return (index % COLS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
grid.forEach(function (value, index) {
|
||||||
|
var col = getCol(index) + 1;
|
||||||
|
var row = getRow(index) + 1;
|
||||||
|
var element = document.getElementById(value);
|
||||||
|
if (element === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
element.classList.remove('c1', 'c2', 'c3', 'r1', 'r2', 'r3');
|
||||||
|
element.classList.add('c' + col, 'r' + row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function swap(a, b) {
|
||||||
|
var tmp = grid[a];
|
||||||
|
grid[a] = grid[b];
|
||||||
|
grid[b] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSpacePossibleMoves(reverseDir) {
|
||||||
|
var index = grid.indexOf(0);
|
||||||
|
var possibleMoves = new Set([MOV_LEFT, MOV_RIGHT, MOV_UP, MOV_DOWN]);
|
||||||
|
if (index % COLS == COLS - 1)
|
||||||
|
possibleMoves.delete(MOV_RIGHT);
|
||||||
|
if (index % COLS == 0)
|
||||||
|
possibleMoves.delete(MOV_LEFT);
|
||||||
|
if (Math.floor(index / ROWS) == 0)
|
||||||
|
possibleMoves.delete(MOV_UP);
|
||||||
|
if (Math.floor(index / ROWS) == ROWS - 1)
|
||||||
|
possibleMoves.delete(MOV_DOWN);
|
||||||
|
possibleMoves.delete(reverseDir);
|
||||||
|
return Array.from(possibleMoves);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveSpace(direction) {
|
||||||
|
var index = grid.indexOf(0);
|
||||||
|
var step = 0;
|
||||||
|
switch (direction) {
|
||||||
|
case MOV_LEFT: case MOV_UP:
|
||||||
|
step = -1;
|
||||||
|
break;
|
||||||
|
case MOV_RIGHT: case MOV_DOWN:
|
||||||
|
step = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
step = 0;
|
||||||
|
}
|
||||||
|
if ([MOV_LEFT, MOV_RIGHT].indexOf(direction) > -1) {
|
||||||
|
// horizontal
|
||||||
|
var inc = index + step;
|
||||||
|
if (inc < 2 || inc >= 0) {
|
||||||
|
swap(index, inc);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// vertical
|
||||||
|
var inc = index + (ROWS * step);
|
||||||
|
if (inc < grid.length || inc >= 0) {
|
||||||
|
swap(index, inc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveHorizontal(index) {
|
||||||
|
var col = getCol(index);
|
||||||
|
if (col == 0) {
|
||||||
|
if (grid[index + 1] == 0)
|
||||||
|
swap(index, index + 1);
|
||||||
|
} else if (col == COLS - 1) {
|
||||||
|
if (grid[index - 1] == 0)
|
||||||
|
swap(index, index - 1);
|
||||||
|
} else {
|
||||||
|
if (grid[index + 1] == 0)
|
||||||
|
swap(index, index + 1);
|
||||||
|
else if (grid[index - 1] == 0)
|
||||||
|
swap(index, index - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveVertical(index) {
|
||||||
|
var row = getRow(index);
|
||||||
|
if (row == 0) {
|
||||||
|
if (grid[index + ROWS] == 0)
|
||||||
|
swap(index, index + ROWS);
|
||||||
|
} else if (row == ROWS - 1) {
|
||||||
|
if (grid[index - ROWS] == 0)
|
||||||
|
swap(index, index - ROWS);
|
||||||
|
} else {
|
||||||
|
if (grid[index + ROWS] == 0)
|
||||||
|
swap(index, index + ROWS);
|
||||||
|
else if (grid[index - ROWS] == 0)
|
||||||
|
swap(index, index - ROWS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shuffle(steps) {
|
||||||
|
var i = 0;
|
||||||
|
var prevDir = -1;
|
||||||
|
tiles.forEach(function (tile) {
|
||||||
|
tile.classList.add('fast');
|
||||||
|
});
|
||||||
|
var loop = setInterval(function () {
|
||||||
|
var reverseDir = -1;
|
||||||
|
if (prevDir == MOV_DOWN)
|
||||||
|
reverseDir = MOV_UP;
|
||||||
|
if (prevDir == MOV_UP)
|
||||||
|
reverseDir = MOV_DOWN;
|
||||||
|
if (prevDir == MOV_LEFT)
|
||||||
|
reverseDir = MOV_RIGHT;
|
||||||
|
if (prevDir == MOV_RIGHT)
|
||||||
|
reverseDir = MOV_LEFT;
|
||||||
|
var possibleMoves = getSpacePossibleMoves(reverseDir);
|
||||||
|
var dir = possibleMoves[Math.floor(Math.random() * possibleMoves.length)];
|
||||||
|
moveSpace(dir);
|
||||||
|
prevDir = dir;
|
||||||
|
i++;
|
||||||
|
if (i >= steps) {
|
||||||
|
clearInterval(loop);
|
||||||
|
enableTiles();
|
||||||
|
document.getElementById('stage').classList.remove('done');
|
||||||
|
buttonShuffle.disabled = false;
|
||||||
|
tiles.forEach(function (tile) {
|
||||||
|
tile.classList.remove('fast');
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, 90);
|
||||||
|
disableTiles();
|
||||||
|
buttonShuffle.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(time) {
|
||||||
|
var minutes = Math.floor(time / 60).toString().padStart(2, '0');
|
||||||
|
var seconds = Math.floor(time % 60).toString().padStart(2, '0');
|
||||||
|
var millis = Math.floor((time % 1) * 100).toString().padStart(2, '0');
|
||||||
|
return minutes + ':' + seconds + '.<small>' + millis + '</small>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTimer() {
|
||||||
|
if (timer !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
startTime = (new Date()).getTime();
|
||||||
|
timer = setInterval(function () {
|
||||||
|
var diff = ((new Date()).getTime() - startTime) / 1000;
|
||||||
|
textTime.innerHTML = formatTime(diff);
|
||||||
|
}, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopTimer() {
|
||||||
|
clearInterval(timer);
|
||||||
|
timer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetTimer() {
|
||||||
|
stopTimer();
|
||||||
|
startTime = 0;
|
||||||
|
textTime.innerHTML = formatTime(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkCompleted() {
|
||||||
|
for (var i = 0; i < grid.length - 2; ++i) {
|
||||||
|
if (grid[i + 1] < grid[i]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.getElementById('stage').classList.add('done');
|
||||||
|
disableTiles();
|
||||||
|
stopTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableTiles() {
|
||||||
|
tiles.forEach(function (button) {
|
||||||
|
button.disabled = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableTiles() {
|
||||||
|
tiles.forEach(function (button) {
|
||||||
|
button.disabled = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resizeStage() {
|
||||||
|
var wrapperStyle = getComputedStyle(wrapper);
|
||||||
|
var paddingLeft = parseInt(wrapperStyle.getPropertyValue('padding-left'));
|
||||||
|
var paddingRight = parseInt(wrapperStyle.getPropertyValue('padding-right'));
|
||||||
|
var width = parseInt(wrapperStyle.getPropertyValue('width'));
|
||||||
|
var adjustedWidth = width - paddingLeft - paddingRight;
|
||||||
|
while (adjustedWidth % 3 != 0) {
|
||||||
|
adjustedWidth--;
|
||||||
|
}
|
||||||
|
stage.style.fontSize = (adjustedWidth / 24) + 'px';
|
||||||
|
stage.style.width = adjustedWidth + 'px';
|
||||||
|
stageSection.style.height = adjustedWidth + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
tiles.forEach(function (button) {
|
||||||
|
button.onclick = function (e) {
|
||||||
|
var tileId = e.target.id;
|
||||||
|
var index = grid.indexOf(parseInt(tileId));
|
||||||
|
moveHorizontal(index);
|
||||||
|
moveVertical(index);
|
||||||
|
render();
|
||||||
|
startTimer();
|
||||||
|
checkCompleted();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
buttonShuffle.onclick = function () {
|
||||||
|
resetTimer();
|
||||||
|
shuffle(SHUFFLES);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function () {
|
||||||
|
resizeStage();
|
||||||
|
render();
|
||||||
|
shuffle(SHUFFLES);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onresize = resizeStage;
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="app.css"/>
|
||||||
|
<title>Number Slide</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="wrapper">
|
||||||
|
<div id="controls">
|
||||||
|
<h1 id="time">00:00.<small>00</small></h1>
|
||||||
|
<button id="shuffle" disabled>Shuffle</button>
|
||||||
|
</div>
|
||||||
|
<main id="stage">
|
||||||
|
<section>
|
||||||
|
<button id="1" class="c1 r1">1</button>
|
||||||
|
<button id="2" class="c2 r1">2</button>
|
||||||
|
<button id="3" class="c3 r1">3</button>
|
||||||
|
<button id="4" class="c1 r2">4</button>
|
||||||
|
<button id="5" class="c2 r2">5</button>
|
||||||
|
<button id="6" class="c3 r2">6</button>
|
||||||
|
<button id="7" class="c1 r3">7</button>
|
||||||
|
<button id="8" class="c2 r3">8</button>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
2021 © <a href="https://johnespiritu.dev">John Espiritu</a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
<script src="app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
Reference in New Issue