1
0
mirror of https://github.com/danog/2048.git synced 2025-01-23 06:12:16 +01:00

Merge remote-tracking branch 'upstream/master' into best-score

Conflicts:
	index.html
	js/application.js
	js/html_actuator.js
This commit is contained in:
Tim Petricola 2014-03-11 12:05:42 -04:00
commit 77654452a7
9 changed files with 464 additions and 146 deletions

View File

@ -5,12 +5,15 @@ Made just for fun. [Play it here!](http://gabrielecirulli.github.io/2048/)
[![Screenshot](http://pictures.gabrielecirulli.com/2048-20140309-234100.png)](http://pictures.gabrielecirulli.com/2048-20140309-234100.png)
That screenshot is fake by, the way. I never reached 2048 :smile:
That screenshot is fake, by the way. I never reached 2048 :smile:
## Contributing
Changes and improvements are more than welcome! Feel free to fork and open a pull request. Please make your changes in a specifically made branch and request to pull on `master`! If you can, please make sure the game fully works before sending the PR, as that will help speed up the process.
Changes and improvements are more than welcome! Feel free to fork and open a pull request. Please make your changes in a specific branch and request to pull into `master`! If you can, please make sure the game fully works before sending the PR, as that will help speed up the process.
You can find the same information in the [contributing guide.](https://github.com/gabrielecirulli/2048/blob/master/CONTRIBUTING.md)
## License
2048 is licensed under the [MIT license.](https://github.com/gabrielecirulli/2048/blob/master/LICENSE.txt)
## Donations
I made this in my spare time, and it's hosted on GitHub (which means I don't have any hosting costs), but if you enjoyed the game and feel like buying me coffee, you can donate at my BTC address: `1Ec6onfsQmoP9kkL3zkpB6c5sA4PVcXU2i`. Thank you very much!

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -5,15 +5,7 @@
<title>2048</title>
<link href="style/main.css" rel="stylesheet" type="text/css">
<script src="js/hammer.min.js"></script>
<script src="js/keyboard_input_manager.js"></script>
<script src="js/html_actuator.js"></script>
<script src="js/grid.js"></script>
<script src="js/tile.js"></script>
<script src="js/local_score_manager.js"></script>
<script src="js/game_manager.js"></script>
<script src="js/application.js"></script>
<link rel="shortcut icon" href="favicon.ico">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
@ -75,8 +67,18 @@
</p>
<hr>
<p>
Created by <a href="http://gabrielecirulli.com" target="_blank">Gabriele Cirulli.</a> Based on <a href="https://itunes.apple.com/us/app/1024!/id823499224" target="_blank">1024 by Veewo Studio.</a>
Created by <a href="http://gabrielecirulli.com" target="_blank">Gabriele Cirulli.</a> Based on <a href="https://itunes.apple.com/us/app/1024!/id823499224" target="_blank">1024 by Veewo Studio</a> and conceptually similar to <a href="http://asherv.com/threes/" target="_blank">Threes by Asher Vollmer.</a>
</p>
</div>
<script src="js/animframe_polyfill.js"></script>
<script src="js/hammer.min.js"></script>
<script src="js/keyboard_input_manager.js"></script>
<script src="js/html_actuator.js"></script>
<script src="js/grid.js"></script>
<script src="js/tile.js"></script>
<script src="js/local_score_manager.js"></script>
<script src="js/game_manager.js"></script>
<script src="js/application.js"></script>
</body>
</html>

26
js/animframe_polyfill.js Normal file
View File

@ -0,0 +1,26 @@
(function() {
var lastTime = 0;
var vendors = ['webkit', 'moz'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame =
window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
}());

View File

@ -1,6 +1,4 @@
document.addEventListener("DOMContentLoaded", function () {
// Wait till the browser is ready to render the game (avoids glitches)
window.requestAnimationFrame(function () {
new GameManager(4, KeyboardInputManager, HTMLActuator, LocalScoreManager);
});
// Wait till the browser is ready to render the game (avoids glitches)
window.requestAnimationFrame(function () {
new GameManager(4, KeyboardInputManager, HTMLActuator, LocalScoreManager);
});

View File

@ -31,11 +31,15 @@ KeyboardInputManager.prototype.listen = function () {
75: 0, // vim keybindings
76: 1,
74: 2,
72: 3
72: 3,
87: 0, // W
68: 1, // D
83: 2, // S
65: 3 // A
};
document.addEventListener("keydown", function (event) {
var modifiers = event.altKey && event.ctrlKey && event.metaKey &&
var modifiers = event.altKey || event.ctrlKey || event.metaKey ||
event.shiftKey;
var mapped = map[event.which];

View File

@ -53,3 +53,10 @@
-webkit-animation-fill-mode: #{$str};
-moz-animation-fill-mode: #{$str};
}
// Media queries
@mixin smaller($width) {
@media screen and (max-width: $width) {
@content;
}
}

View File

@ -140,6 +140,16 @@ hr {
100% {
opacity: 1; } }
.game-container .game-message a {
display: inline-block;
background: #8f7a66;
border-radius: 3px;
padding: 0 20px;
text-decoration: none;
color: #f9f6f2;
height: 40px;
line-height: 42px; }
.game-container {
margin-top: 40px;
position: relative;
@ -179,14 +189,6 @@ hr {
display: block;
margin-top: 59px; }
.game-container .game-message a {
display: inline-block;
background: #8f7a66;
border-radius: 3px;
padding: 0 20px;
text-decoration: none;
color: #f9f6f2;
height: 40px;
line-height: 42px;
margin-left: 9px; }
.game-container .game-message.game-won {
background: rgba(237, 194, 46, 0.5);
@ -222,20 +224,9 @@ hr {
z-index: 2; }
.tile {
background: red;
width: 106.25px;
height: 106.25px;
border-radius: 3px;
background: #eee4da;
text-align: center;
line-height: 116.25px;
font-size: 55px;
font-weight: bold;
z-index: 10;
-webkit-transition: 100ms ease-in-out;
-moz-transition: 100ms ease-in-out;
-webkit-transition-property: top, left;
-moz-transition-property: top, left; }
line-height: 116.25px; }
.tile.tile-position-1-1 {
position: absolute;
left: 0px;
@ -300,6 +291,18 @@ hr {
position: absolute;
left: 364px;
top: 364px; }
.tile {
border-radius: 3px;
background: #eee4da;
text-align: center;
font-weight: bold;
z-index: 10;
font-size: 55px;
-webkit-transition: 100ms ease-in-out;
-moz-transition: 100ms ease-in-out;
-webkit-transition-property: top, left;
-moz-transition-property: top, left; }
.tile.tile-2 {
background: #eee4da;
box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0), inset 0 0 0 1px rgba(255, 255, 255, 0); }
@ -323,26 +326,41 @@ hr {
background: #edcf72;
box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.2381), inset 0 0 0 1px rgba(255, 255, 255, 0.14286);
font-size: 45px; }
@media screen and (max-width: 480px) {
.tile.tile-128 {
font-size: 25px; } }
.tile.tile-256 {
color: #f9f6f2;
background: #edcc61;
box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.31746), inset 0 0 0 1px rgba(255, 255, 255, 0.19048);
font-size: 45px; }
@media screen and (max-width: 480px) {
.tile.tile-256 {
font-size: 25px; } }
.tile.tile-512 {
color: #f9f6f2;
background: #edc850;
box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.39683), inset 0 0 0 1px rgba(255, 255, 255, 0.2381);
font-size: 45px; }
@media screen and (max-width: 480px) {
.tile.tile-512 {
font-size: 25px; } }
.tile.tile-1024 {
color: #f9f6f2;
background: #edc53f;
box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.47619), inset 0 0 0 1px rgba(255, 255, 255, 0.28571);
font-size: 35px; }
@media screen and (max-width: 480px) {
.tile.tile-1024 {
font-size: 15px; } }
.tile.tile-2048 {
color: #f9f6f2;
background: #edc22e;
box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.55556), inset 0 0 0 1px rgba(255, 255, 255, 0.33333);
font-size: 35px; }
@media screen and (max-width: 480px) {
.tile.tile-2048 {
font-size: 15px; } }
@-webkit-keyframes appear {
0% {
@ -434,3 +452,180 @@ hr {
.game-explanation {
margin-top: 50px; }
@media screen and (max-width: 480px) {
html, body {
font-size: 15px; }
body {
margin: 20px 0;
padding: 0 20px; }
h1.title {
font-size: 50px; }
.container {
width: 280px;
margin: 0 auto; }
.score-container {
margin-top: 0; }
.heading {
margin-bottom: 10px; }
.game-container {
margin-top: 40px;
position: relative;
padding: 10px;
cursor: default;
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
background: #bbada0;
border-radius: 6px;
width: 280px;
height: 280px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box; }
.game-container .game-message {
display: none;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(238, 228, 218, 0.5);
z-index: 100;
text-align: center;
-webkit-animation: fade-in 800ms ease 1200ms;
-moz-animation: fade-in 800ms ease 1200ms;
-webkit-animation-fill-mode: both;
-moz-animation-fill-mode: both; }
.game-container .game-message p {
font-size: 60px;
font-weight: bold;
height: 60px;
line-height: 60px;
margin-top: 222px; }
.game-container .game-message .lower {
display: block;
margin-top: 59px; }
.game-container .game-message a {
margin-left: 9px; }
.game-container .game-message.game-won {
background: rgba(237, 194, 46, 0.5);
color: #f9f6f2; }
.game-container .game-message.game-won, .game-container .game-message.game-over {
display: block; }
.grid-container {
position: absolute;
z-index: 1; }
.grid-row {
margin-bottom: 10px; }
.grid-row:last-child {
margin-bottom: 0; }
.grid-row:after {
content: "";
display: block;
clear: both; }
.grid-cell {
width: 57.5px;
height: 57.5px;
margin-right: 10px;
float: left;
border-radius: 3px;
background: rgba(238, 228, 218, 0.35); }
.grid-cell:last-child {
margin-right: 0; }
.tile-container {
position: absolute;
z-index: 2; }
.tile {
width: 57.5px;
height: 57.5px;
line-height: 67.5px; }
.tile.tile-position-1-1 {
position: absolute;
left: 0px;
top: 0px; }
.tile.tile-position-1-2 {
position: absolute;
left: 0px;
top: 68px; }
.tile.tile-position-1-3 {
position: absolute;
left: 0px;
top: 135px; }
.tile.tile-position-1-4 {
position: absolute;
left: 0px;
top: 203px; }
.tile.tile-position-2-1 {
position: absolute;
left: 68px;
top: 0px; }
.tile.tile-position-2-2 {
position: absolute;
left: 68px;
top: 68px; }
.tile.tile-position-2-3 {
position: absolute;
left: 68px;
top: 135px; }
.tile.tile-position-2-4 {
position: absolute;
left: 68px;
top: 203px; }
.tile.tile-position-3-1 {
position: absolute;
left: 135px;
top: 0px; }
.tile.tile-position-3-2 {
position: absolute;
left: 135px;
top: 68px; }
.tile.tile-position-3-3 {
position: absolute;
left: 135px;
top: 135px; }
.tile.tile-position-3-4 {
position: absolute;
left: 135px;
top: 203px; }
.tile.tile-position-4-1 {
position: absolute;
left: 203px;
top: 0px; }
.tile.tile-position-4-2 {
position: absolute;
left: 203px;
top: 68px; }
.tile.tile-position-4-3 {
position: absolute;
left: 203px;
top: 135px; }
.tile.tile-position-4-4 {
position: absolute;
left: 203px;
top: 203px; }
.game-container {
margin-top: 20px; }
.tile {
font-size: 35px; }
.game-message p {
font-size: 30px !important;
height: 30px !important;
line-height: 30px !important;
margin-top: 90px !important; }
.game-message .lower {
margin-top: 30px !important; } }

View File

@ -153,145 +153,159 @@ hr {
}
}
.game-container {
margin-top: 40px;
position: relative;
padding: $grid-spacing;
// Styles for buttons
@mixin button {
display: inline-block;
background: darken($game-container-background, 20%);
border-radius: 3px;
padding: 0 20px;
text-decoration: none;
color: $bright-text-color;
height: 40px;
line-height: 42px;
}
cursor: default;
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
// Game field mixin used to render CSS at different width
@mixin game-field {
.game-container {
margin-top: 40px;
position: relative;
padding: $grid-spacing;
background: $game-container-background;
border-radius: $tile-border-radius * 2;
width: $field-width;
height: $field-width;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
cursor: default;
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
.game-message {
display: none;
background: $game-container-background;
border-radius: $tile-border-radius * 2;
width: $field-width;
height: $field-width;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
.game-message {
display: none;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba($tile-color, .5);
z-index: 100;
text-align: center;
p {
font-size: 60px;
font-weight: bold;
height: 60px;
line-height: 60px;
margin-top: 222px;
// height: $field-width;
// line-height: $field-width;
}
.lower {
display: block;
margin-top: 59px;
}
a {
@include button;
margin-left: 9px;
// margin-top: 59px;
}
@include animation(fade-in 800ms ease $transition-speed * 12);
@include animation-fill-mode(both);
&.game-won {
background: rgba($tile-gold-color, .5);
color: $bright-text-color;
}
&.game-won, &.game-over {
display: block;
}
}
}
.grid-container {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba($tile-color, .5);
z-index: 100;
z-index: 1;
}
text-align: center;
.grid-row {
margin-bottom: $grid-spacing;
p {
font-size: 60px;
font-weight: bold;
height: 60px;
line-height: 60px;
margin-top: 222px;
// height: $field-width;
// line-height: $field-width;
&:last-child {
margin-bottom: 0;
}
.lower {
&:after {
content: "";
display: block;
margin-top: 59px;
clear: both;
}
}
a {
display: inline-block;
background: darken($game-container-background, 20%);
border-radius: 3px;
padding: 0 20px;
text-decoration: none;
color: $bright-text-color;
height: 40px;
line-height: 42px;
margin-left: 9px;
// margin-top: 59px;
.grid-cell {
width: $tile-size;
height: $tile-size;
margin-right: $grid-spacing;
float: left;
border-radius: $tile-border-radius;
background: rgba($tile-color, .35);
&:last-child {
margin-right: 0;
}
}
@include animation(fade-in 800ms ease $transition-speed * 12);
@include animation-fill-mode(both);
.tile-container {
position: absolute;
z-index: 2;
}
&.game-won {
background: rgba($tile-gold-color, .5);
color: $bright-text-color;
}
.tile {
width: $tile-size;
height: $tile-size;
line-height: $tile-size + 10px;
&.game-won, &.game-over {
display: block;
// Build position classes
@for $x from 1 through $grid-row-cells {
@for $y from 1 through $grid-row-cells {
&.tile-position-#{$x}-#{$y} {
position: absolute;
left: round(($tile-size + $grid-spacing) * ($x - 1));
top: round(($tile-size + $grid-spacing) * ($y - 1));
}
}
}
}
}
.grid-container {
position: absolute;
z-index: 1;
}
.grid-row {
margin-bottom: $grid-spacing;
&:last-child {
margin-bottom: 0;
}
&:after {
content: "";
display: block;
clear: both;
}
}
.grid-cell {
width: $tile-size;
height: $tile-size;
margin-right: $grid-spacing;
float: left;
border-radius: $tile-border-radius;
background: rgba($tile-color, .35);
&:last-child {
margin-right: 0;
}
}
.tile-container {
position: absolute;
z-index: 2;
}
// End of game-field mixin
@include game-field;
.tile {
background: red;
width: $tile-size;
height: $tile-size;
border-radius: $tile-border-radius;
background: $tile-color;
text-align: center;
line-height: $tile-size + 10px;
font-size: 55px;
font-weight: bold;
z-index: 10;
font-size: 55px;
@include transition($transition-speed ease-in-out);
@include transition-property(top, left);
// Build position classes
@for $x from 1 through $grid-row-cells {
@for $y from 1 through $grid-row-cells {
&.tile-position-#{$x}-#{$y} {
position: absolute;
left: round(($tile-size + $grid-spacing) * ($x - 1));
top: round(($tile-size + $grid-spacing) * ($y - 1));
}
}
}
$base: 2;
$exponent: 1;
$limit: 11;
@ -345,8 +359,17 @@ hr {
// Adjust font size for bigger numbers
@if $power >= 100 and $power < 1000 {
font-size: 45px;
// Media queries placed here to avoid carrying over the rest of the logic
@include smaller(480px) {
font-size: 25px;
}
} @else if $power >= 1000 {
font-size: 35px;
@include smaller(480px) {
font-size: 15px;
}
}
}
@ -403,3 +426,63 @@ hr {
.game-explanation {
margin-top: 50px;
}
@include smaller(480px) {
// Redefine variables for smaller screens
$field-width: 280px;
$grid-spacing: 10px;
$grid-row-cells: 4;
$tile-size: ($field-width - $grid-spacing * ($grid-row-cells + 1)) / $grid-row-cells;
$tile-border-radius: 3px;
html, body {
font-size: 15px;
}
body {
margin: 20px 0;
padding: 0 20px;
}
h1.title {
font-size: 50px;
}
.container {
width: $field-width;
margin: 0 auto;
}
.score-container {
margin-top: 0;
}
.heading {
margin-bottom: 10px;
}
// Render the game field at the right width
@include game-field;
.game-container {
margin-top: 20px;
}
// Rest of the font-size adjustments in the tile class
.tile {
font-size: 35px;
}
.game-message {
p {
font-size: 30px !important;
height: 30px !important;
line-height: 30px !important;
margin-top: 90px !important;
}
.lower {
margin-top: 30px !important;
}
}
}