2048 is a sliding-block puzzle game inspired by the original 2048 by Gabrielle Cirulli.
In order to enable for consistent gameplay and an interactive UI, 2048 separates the game logic from the game rendering.
The game logic is handled in vanilla JavaScript using object-oriented design. A game
, grid
and tile
class are responsible for maintaining the current state of the game's logic.
In rendering the game state to the DOM, 2048 implements a GameContainer
React Component which links the game logic state to three functional React Components Header
, Tiles
and Tile
which are responsible for displaying the current game state.
The GameContainer
is responsible for listening to key events from the user, updating the game state and only re-rendering when the state of the game has changed.
By leveraging React's virtual DOM 'diffing' algorithm, only those Tiles
whose state (position or value) have been modified are modified and re-rendered.
const Tile = ({ tile }) => {
const merged = tile.merged ? 'merged' : '';
return (
<div className={ `tile pos-${tile.row}-${tile.col} value-${tile.value} ${ merged }` }>
<span>{ tile.value }</span>
</div>
);
}
Part of what makes 2048 so addicting is it's incredibly interactive UI/UX. In order to slide tiles up, down, left or right, the user simply presses the corresponding arrow key, or swipes in the corresponding direction on mobile.
From tiles merging together to new ones appearing, 2048 utilizes CSS transitions, animations and ReactCSSTransitionGroup for an engaging and intuitive user experience.
/* ANIMATIONS */
.merged {
animation: merge 100ms linear;
}
@-webkit-keyframes merge {
0% { transform: scale(1, 1) }
50% { transform: scale(1.2, 1.2) }
100% { transform: scale(1, 1) }
}
@keyframes merge {
0% { transform: scale(1, 1) }
50% { transform: scale(1.2, 1.2) }
100% { transform: scale(1, 1) }
}
In order for mobile users to be able to play, 2048 implements touch event handlers to track whether a mobile users swipes up, down, left or right. Both touch and keydown events are bound in an InputHandler
class that calls relevant methods to update the game state.
bindTouchEvents(game, updateGame) {
let startX;
let startY;
let endX;
let endY;
document.addEventListener('touchstart', event => {
let touchObj = event.changedTouches[0];
startX = touchObj.pageX;
startY = touchObj.pageY;
});
document.addEventListener('touchmove', event => {
event.preventDefault();
event.stopPropagation();
}, { passive: false });
document.addEventListener('touchend', event => {
let touchObj = event.changedTouches[0];
endX = touchObj.pageX;
endY = touchObj.pageY;
let dx = endX - startX;
let dy = endY - startY;
if (Math.abs(dx) > 25 || Math.abs(dy) > 25) {
this.handleSwipe(dx, dy);
}
});
}
For even more beautiful gameplay, 2048 includes a dark mode that allows users to switch the UI color scheme dynamically during game play using a toggle switch.