JS實現2048小游戲源碼
效果圖:
代碼如下,復制即可使用:
(適用瀏覽器:360、FireFox、Chrome、Opera、傲游、搜狗、世界之窗. 不支持Safari、IE8及以下瀏覽器。)
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=0"> <title>使用JS實現2048小游戲</title> <!-- 此處需要自己修改為自己的css路徑 --> <link rel="stylesheet" href="css/reset.min.css"> <link rel="stylesheet" href="css/style.css"> </head> <body> <header> <div class="container"> <h1><span>2</span><span>0</span><span>4</span><span>8</span></h1> <p class="inspired">by the原2048的靈感。</p> </div> </header> <div class="container"> <div class="directions"> <p><strong>如何玩:</strong> 使用你的箭頭鍵移動瓷磚。當兩個瓦片相互滑動時,它們合並成一個!</p> </div> <div class="scores"> <div class="score-container best-score"> 最佳: <div class="score"> <div id="bestScore">0</div> </div> </div> <div class="score-container"> 分數: <div class="score"> <div id="score">0</div> <div class="add" id="add"></div> </div> </div> </div> <div class="game"> <div id="tile-container" class="tile-container"></div> <div class="end" id="end">游戲結束<div class="monkey"></div><button class="btn not-recommended__item js-restart-btn" id="try-again">再試一次</button></div> </div> <div class="not-recommended"> <button class="btn not-recommended__item js-restart-btn" id="restart">重新啟動游戲</button> </div> </div> <!-- 此處需要自己修改JS路徑 --> <script src="js/index.js"></script> <div style="text-align:center;margin:10px 0; font:normal 14px/24px 'MicroSoft YaHei';"> <p>適用瀏覽器:360、FireFox、Chrome、Opera、傲游、搜狗、世界之窗. 不支持Safari、IE8及以下瀏覽器。</p> </div> </body> </html>
reset.min.css
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}table{border-collapse:collapse;border-spacing:0}
style.css
@charset "UTF-8";
* {
box-sizing: border-box;
}
a {
color: #1B9AAA;
text-decoration: none;
border-bottom: 1px solid currentColor;
}
a:hover {
color: #14727e;
}
a:focus, a:active {
color: #0d4a52;
}
body,
html {
position: relative;
width: 100%;
height: 100%;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
font-family: "Arvo", Helvetica, sans-serif;
font-family: 12px;
color: #555;
background: #F8FFE5;
}
strong {
font-weight: bold;
}
p {
line-height: 1.6;
}
.inspired {
margin-top: 1em;
font-size: 0.9rem;
color: #9a9a95;
}
header {
color: #F8FFE5;
text-align: center;
}
header span {
display: inline-block;
box-sizing: border-box;
width: 4rem;
height: 4rem;
line-height: 4rem;
margin: 0 0.4rem;
background: #FFC43D;
}
header span:nth-of-type(2) {
background: #EF476F;
}
header span:nth-of-type(3) {
background: #1B9AAA;
}
header span:nth-of-type(4) {
background: #06D6A0;
}
h1 {
font-size: 2.2rem;
}
.directions {
padding: 2rem;
border-top: 1px solid #9a9a95;
border-bottom: 1px solid #9a9a95;
}
.container {
margin: 0 auto;
padding-bottom: 3.5rem;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
width: 100%;
max-width: 550px;
text-align: center;
}
header .container {
padding: 0;
padding: 2rem 4rem;
max-width: 900px;
}
.scores {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
.score-container {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
margin: 1.8rem;
font-size: 1.2rem;
line-height: 1;
color: #555;
}
.score-container.best-score {
color: #9a9a95;
}
.score {
margin-left: 1rem;
position: relative;
font-weight: bold;
font-size: 1.5rem;
vertical-align: middle;
text-align: right;
}
.game {
position: relative;
margin: 0 auto;
background: #9a9a95;
padding: 7px;
display: inline-block;
border-radius: 3px;
box-sizing: border-box;
}
.tile-container {
border-radius: 6px;
position: relative;
width: 400px;
height: 400px;
}
.tile, .background {
display: block;
color: #F8FFE5;
position: absolute;
width: 100px;
height: 100px;
box-sizing: border-box;
text-align: center;
}
.background {
z-index: 1;
text-align: center;
border: 7px solid #9a9a95;
background-color: #F8FFE5;
}
.tile {
opacity: 0;
z-index: 2;
background: #FFC43D;
color: #F8FFE5;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
font-size: 1.8rem;
align-items: center;
-webkit-transition: 110ms ease-in-out;
transition: 110ms ease-in-out;
border-radius: 3px;
border: 7px solid #9a9a95;
box-sizing: border-box;
}
.tile--4 {
background: #EF476F;
color: #F8FFE5;
}
.tile--8 {
background: #1B9AAA;
color: #F8FFE5;
}
.tile--16 {
background: #06D6A0;
color: #F8FFE5;
}
.tile--32 {
background: #f37694;
color: #F8FFE5;
}
.tile--64 {
background: #22c2d6;
color: #F8FFE5;
}
.tile--128 {
background: #17f8be;
color: #F8FFE5;
}
.tile--256 {
background: #ffd470;
color: #F8FFE5;
}
.tile--512 {
background: #eb184a;
color: #F8FFE5;
}
.tile--1024 {
background: #14727e;
color: #F8FFE5;
}
.tile--2048 {
background: #05a47b;
color: #F8FFE5;
}
.tile--pop {
-webkit-animation: pop 0.3s ease-in-out;
animation: pop 0.3s ease-in-out;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
.tile--shrink {
-webkit-animation: shrink 0.5s ease-in-out;
animation: shrink 0.5s ease-in-out;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
.add {
position: absolute;
opacity: 0;
left: 120%;
top: 0;
font-size: 1rem;
color: #1B9AAA;
}
.add.active {
-webkit-animation: add 0.8s ease-in-out;
animation: add 0.8s ease-in-out;
}
@-webkit-keyframes add {
0% {
opacity: 1;
top: 0;
}
100% {
opacity: 0;
top: -100%;
}
}
@keyframes add {
0% {
opacity: 1;
top: 0;
}
100% {
opacity: 0;
top: -100%;
}
}
@-webkit-keyframes pop {
0% {
-webkit-transform: scale(0.5);
transform: scale(0.5);
opacity: 0;
}
90% {
-webkit-transform: scale(1.1);
transform: scale(1.1);
opacity: 1;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
}
@keyframes pop {
0% {
-webkit-transform: scale(0.5);
transform: scale(0.5);
opacity: 0;
}
90% {
-webkit-transform: scale(1.1);
transform: scale(1.1);
opacity: 1;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
}
@-webkit-keyframes shrink {
0% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
100% {
-webkit-transform: scale(0.9);
transform: scale(0.9);
opacity: 0.9;
}
}
@keyframes shrink {
0% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
100% {
-webkit-transform: scale(0.9);
transform: scale(0.9);
opacity: 0.9;
}
}
.end {
opacity: 0;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
background: rgba(85, 85, 85, 0.9);
color: white;
font-size: 2rem;
-webkit-transition: opacity 0.3s ease-in-out;
transition: opacity 0.3s ease-in-out;
}
.end btn {
margin-top: 1rem;
}
.end.active {
opacity: 1;
z-index: 1000;
}
.monkey {
font-size: 3rem;
margin: 1rem 0;
}
.btn {
font-family: inherit;
font-size: 1rem;
border: none;
background: #1B9AAA;
letter-spacing: 1px;
color: white;
font-weight: 300;
padding: 0.9em 1.5em;
border-radius: 3px;
border: 1px solid transparent;
cursor: pointer;
}
.btn:hover {
background-color: #14727e;
}
.btn:active {
background-color: #0d4a52;
}
.btn:focus {
box-shadow: 0 0 10px #0d4a52 inset;
outline: none;
}
.not-recommended {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
margin-top: 3rem;
}
.not-recommended * + * {
margin-left: 10px;
}
.not-recommended__item + .not-recommended__annotation:before {
font-size: 30px;
content: "😐";
}
.not-recommended__item:hover + .not-recommended__annotation:before {
content: "😟";
}
.not-recommended__item:focus + .not-recommended__annotation:before {
content: "😄";
}
.not-recommended__item:active + .not-recommended__annotation:before {
content: "😨";
}
index.js
'use strict'; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var game = null; var bestScore = 0; var scoreDiv = document.getElementById('score'); var bestScoreDiv = document.getElementById('bestScore'); var addDiv = document.getElementById('add'); var endDiv = document.getElementById('end'); var size = 4; var nextId = 1; var score = 0; function initGame() { game = Array(size * size).fill(null); // 4 x 4 grid, represented as an array initBestScore(); } function initBestScore() { bestScore = localStorage.getItem('bestScore') || 0; bestScoreDiv.innerHTML = bestScore; } function updateDOM(before, after) { var newElements = getNewElementsDOM(before, after); var existingElements = getExistingElementsDOM(before, after); var mergedTiles = getMergedTiles(after); removeElements(mergedTiles); drawGame(newElements, true); drawGame(existingElements); } function removeElements(mergedTiles) { for (var _iterator = mergedTiles, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { var _ref; if (_isArray) { if (_i >= _iterator.length) break; _ref = _iterator[_i++]; } else { _i = _iterator.next(); if (_i.done) break; _ref = _i.value; } var tile = _ref; var _loop = function _loop() { if (_isArray2) { if (_i2 >= _iterator2.length) return 'break'; _ref2 = _iterator2[_i2++]; } else { _i2 = _iterator2.next(); if (_i2.done) return 'break'; _ref2 = _i2.value; } var id = _ref2; var currentElm = document.getElementById(id); positionTile(tile, currentElm); currentElm.classList.add('tile--shrink'); setTimeout(function () { currentElm.remove(); }, 100); }; for (var _iterator2 = tile.mergedIds, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { var _ref2; var _ret = _loop(); if (_ret === 'break') break; } } } function getMergedTiles(after) { return after.filter(function (tile) { return tile && tile.mergedIds; }); } function getNewElementsDOM(before, after) { var beforeIds = before.filter(function (tile) { return tile; }).map(function (tile) { return tile.id; }); var newElements = after.filter(function (tile) { return tile && beforeIds.indexOf(tile.id) === -1; }); return newElements; } function getExistingElementsDOM(before, after) { var beforeIds = before.filter(function (tile) { return tile; }).map(function (tile) { return tile.id; }); var existingElements = after.filter(function (tile) { return tile && beforeIds.indexOf(tile.id) !== -1; }); return existingElements; } function drawBackground() { var tileContainer = document.getElementById('tile-container'); tileContainer.innerHTML = ''; for (var i = 0; i < game.length; i++) { var tile = game[i]; var tileDiv = document.createElement('div'); var x = i % size; var y = Math.floor(i / size); tileDiv.style.top = y * 100 + 'px'; tileDiv.style.left = x * 100 + 'px'; tileDiv.classList.add("background"); tileContainer.appendChild(tileDiv); } } function positionTile(tile, elm) { var x = tile.index % size; var y = Math.floor(tile.index / size); elm.style.top = y * 100 + 'px'; elm.style.left = x * 100 + 'px'; } function drawGame(tiles, isNew) { var tileContainer = document.getElementById('tile-container'); for (var i = 0; i < tiles.length; i++) { var tile = tiles[i]; if (tile) { if (isNew) { (function () { var tileDiv = document.createElement('div'); positionTile(tile, tileDiv); tileDiv.classList.add('tile', 'tile--' + tile.value); tileDiv.id = tile.id; setTimeout(function () { tileDiv.classList.add("tile--pop"); }, tile.mergedIds ? 1 : 150); tileDiv.innerHTML = '<p>' + tile.value + '</p>'; tileContainer.appendChild(tileDiv); })(); } else { var currentElement = document.getElementById(tile.id); positionTile(tile, currentElement); } } } } function gameOver() { if (game.filter(function (number) { return number === null; }).length === 0) { var sameNeighbors = game.find(function (tile, i) { var isRightSame = game[i + 1] && (i + 1) % 4 !== 0 ? tile.value === game[i + 1].value : false; var isDownSame = game[i + 4] ? tile.value === game[i + 4].value : false; if (isRightSame || isDownSame) { return true; } return false; }); return !sameNeighbors; } } function generateNewNumber() { // 0.9 probability of 2, 0.1 probability of 4 var p = Math.random() * 100; return p <= 90 ? 2 : 4; } function addRandomNumber() { // Adds either a 2 or a 4 to an empty position in the game array var emptyCells = game.map(function (_, index) { return index; }).filter(function (index) { return game[index] === null; }); if (emptyCells.length === 0) { return; } var newPos = emptyCells[Math.floor(Math.random() * emptyCells.length)]; var newObj = { id: nextId++, index: newPos, value: generateNewNumber() }; game.splice(newPos, 1, newObj); } function getIndexForPoint(x, y) { return y * size + x; } function reflectGrid(grid) { var reflectedGame = Array(size * size).fill(0); for (var row = 0; row < size; row++) { for (var col = 0; col < size; col++) { var index1 = getIndexForPoint(col, row); var index2 = getIndexForPoint(size - col - 1, row); reflectedGame[index1] = grid[index2]; } } return reflectedGame; } function rotateLeft90Deg(grid) { var rotatedGame = Array(size * size).fill(0); for (var row = 0; row < size; row++) { for (var col = 0; col < size; col++) { var index1 = getIndexForPoint(col, row); var index2 = getIndexForPoint(size - 1 - row, col); rotatedGame[index1] = grid[index2]; } } return rotatedGame; } function rotateRight90Deg(grid) { var rotatedGame = Array(size * size).fill(0); for (var row = 0; row < size; row++) { for (var col = 0; col < size; col++) { var index1 = getIndexForPoint(col, row); var index2 = getIndexForPoint(row, size - 1 - col); rotatedGame[index1] = grid[index2]; } } return rotatedGame; } /* For any cell whose neighbor to the right is empty, move that cell to the right. For any cell whose neighbor to the right is equal to the same value, combine the values together (e.g. 2+2 = 4) */ function shiftGameRight(gameGrid) { // reflect game grid var reflectedGame = reflectGrid(gameGrid); // shift left reflectedGame = shiftGameLeft(reflectedGame); // reflect back return reflectGrid(reflectedGame); } function shiftGameLeft(gameGrid) { var newGameState = []; var totalAdd = 0; // for rows for (var i = 0; i < size; i++) { // for columns var firstPos = 4 * i; var lastPos = size + 4 * i; var currentRow = gameGrid.slice(firstPos, lastPos); var filteredRow = currentRow.filter(function (row) { return row; }); for (var _iterator3 = filteredRow, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { var _ref3; if (_isArray3) { if (_i3 >= _iterator3.length) break; _ref3 = _iterator3[_i3++]; } else { _i3 = _iterator3.next(); if (_i3.done) break; _ref3 = _i3.value; } var row = _ref3; delete row.mergedIds; } for (var j = 0; j < filteredRow.length - 1; j++) { if (filteredRow[j].value === filteredRow[j + 1].value) { var sum = filteredRow[j].value * 2; filteredRow[j] = { id: nextId++, mergedIds: [filteredRow[j].id, filteredRow[j + 1].id], value: sum }; filteredRow.splice(j + 1, 1); score += sum; totalAdd += sum; } } while (filteredRow.length < size) { filteredRow.push(null); }; newGameState = [].concat(newGameState, filteredRow); } if (totalAdd > 0) { scoreDiv.innerHTML = score; addDiv.innerHTML = '+' + totalAdd; addDiv.classList.add('active'); setTimeout(function () { addDiv.classList.remove("active"); }, 800); if (score > bestScore) { localStorage.setItem('bestScore', score); initBestScore(); } } return newGameState; } function shiftGameUp(gameGrid) { var rotatedGame = rotateLeft90Deg(gameGrid); rotatedGame = shiftGameLeft(rotatedGame); return rotateRight90Deg(rotatedGame); } function shiftGameDown(gameGrid) { var rotatedGame = rotateRight90Deg(gameGrid); rotatedGame = shiftGameLeft(rotatedGame); return rotateLeft90Deg(rotatedGame); } var buttons = document.querySelectorAll(".js-restart-btn"); var length = buttons.length; for (var i = 0; i < length; i++) { if (document.addEventListener) { buttons[i].addEventListener("click", function () { newGameStart(); }); } else { buttons[i].attachEvent("onclick", function () { newGameStart(); }); }; }; document.addEventListener("keydown", handleKeypress); document.addEventListener('touchstart', handleTouchStart, false); document.addEventListener('touchmove', handleTouchMove, false); var xDown = null; var yDown = null; function handleTouchStart(evt) { xDown = evt.touches[0].clientX; yDown = evt.touches[0].clientY; }; function handleTouchMove(evt) { var prevGame = [].concat(game); if (!xDown || !yDown) { return; } var xUp = evt.touches[0].clientX; var yUp = evt.touches[0].clientY; var xDiff = xDown - xUp; var yDiff = yDown - yUp; if (Math.abs(xDiff) > Math.abs(yDiff)) { if (xDiff > 0) { game = shiftGameLeft(game); } else { game = shiftGameRight(game); } } else { if (yDiff > 0) { game = shiftGameUp(game); } else { game = shiftGameDown(game); } } game = game.map(function (tile, index) { if (tile) { return _extends({}, tile, { index: index }); } else { return null; } }); addRandomNumber(); updateDOM(prevGame, game); if (gameOver()) { setTimeout(function () { endDiv.classList.add('active'); }, 800); return; } xDown = null; yDown = null; }; function handleKeypress(evt) { var modifiers = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey; var whichKey = event.which; var prevGame = [].concat(game); if (!modifiers) { event.preventDefault(); switch (whichKey) { case 37: game = shiftGameLeft(game); break; case 38: game = shiftGameUp(game); break; case 39: game = shiftGameRight(game); break; case 40: game = shiftGameDown(game); break; } game = game.map(function (tile, index) { if (tile) { return _extends({}, tile, { index: index }); } else { return null; } }); addRandomNumber(); updateDOM(prevGame, game); if (gameOver()) { setTimeout(function () { endDiv.classList.add('active'); }, 800); return; } } } function newGameStart() { document.getElementById('tile-container').innerHTML = ''; endDiv.classList.remove('active'); score = 0; scoreDiv.innerHTML = score; initGame(); drawBackground(); var previousGame = [].concat(game); addRandomNumber(); addRandomNumber(); updateDOM(previousGame, game); } newGameStart();
如果有更好的方法或更多的功能,可以和我們大家一起分享哦,如有錯誤,歡迎聯系我改正,非常感謝!!!