Skip to content

Instantly share code, notes, and snippets.

@makzan
Last active August 29, 2015 14:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save makzan/0f8d9937154f341b3257 to your computer and use it in GitHub Desktop.
Save makzan/0f8d9937154f341b3257 to your computer and use it in GitHub Desktop.
https://www.dropbox.com/s/nmh10tmsdkto1u1/Testing%20Client%20A.zip?dl=0
var gameSetting = {
rowsOptions: [2, 3, 4],
rotations: [0, 90, 180, 270],
areaWidth: 300,
areaHeight: 300,
};
// DATA only
var game = {
level: 1,
imageSrc: '',
pieces: [
// mock data, logic will generate data and replace here.
{pieceId:1, order: 1, left: 0, top: 0, rotation: 0, imgSrc: ''},
{pieceId:2, order: 2, left: 0, top: 0, rotation: 0, imgSrc: ''},
{pieceId:3, order: 3, left: 0, top: 0, rotation: 0, imgSrc: ''},
{pieceId:4, order: 4, left: 0, top: 0, rotation: 0, imgSrc: ''},
],
completedCount: 0
}
var gameStorage = {
save: function(game) {
localStorage["gameSave"] = JSON.stringify(game);
},
// return game data from JSON format
load: function() {
if (localStorage["gameSave"] !== undefined) {
return JSON.parse(localStorage["gameSave"]);
}
return undefined;
},
clear: function() {
// TODO: clear the localStorage["gameSave"]
}
}
// cropping helper
var cropper = {
canvas: $('#cropper')[0],
// crop the gameImage into 300x300 (follow the area)
makeImage: function(image) {
var w = gameSetting.areaWidth;
var h = gameSetting.areaHeight;
var img = new Image();
img.src = image;
// assume img loads immediately.
this.canvas.width = w;
this.canvas.height = h;
var ctx = this.canvas.getContext('2d');
// check width, height
if (img.width > img.height) {
var cropWidth = img.height / h * w;
var cropX = (img.width - img.height)/2;
ctx.drawImage(img, cropX, 0, cropWidth, img.height, 0, 0, w, h);
} else {
var cropHeight = img.width / w * h;
var cropY = (img.height - img.width)/2;
ctx.drawImage(img, 0, cropY, img.width, cropHeight, 0, 0, w, h);
}
game.imageSrc = this.canvas.toDataURL();
},
cropImage: function(cropX, cropY, targetWidth, targetHeight) {
// console.log(cropX, cropY, targetWidth, targetHeight);
this.canvas.width = targetWidth;
this.canvas.height = targetHeight;
var ctx = this.canvas.getContext('2d');
var img = new Image();
img.src = game.imageSrc;
ctx.drawImage(img, cropX, cropY, targetWidth, targetHeight, 0, 0, targetWidth, targetHeight);
return this.canvas.toDataURL();
}
}
// view related
var gameView = {
render: function() {
gameView.renderStartArea();
gameView.renderDestArea();
},
createDestDropGrid: function() {
// remove existings
$('.dest-piece').remove();
var rowsCount = gameSetting.rowsOptions[game.level];
var pieceWidth = gameSetting.areaWidth / rowsCount;
var idCount = 1;
for (i = 0; i < rowsCount; i++) {
for (j = 0; j < rowsCount; j++) {
var hiddenElement = $('<div>');
hiddenElement.addClass('dest-piece');
hiddenElement.data('correctId', idCount);
// dimension
hiddenElement.css('width', pieceWidth);
hiddenElement.css('height', pieceWidth);
// position
hiddenElement.css('left', i * pieceWidth);
hiddenElement.css('top', j * pieceWidth);
$('#dest-area').append(hiddenElement);
hiddenElement.droppable({
accept: function(elements){
var draggingElement = elements[0];
if ($(draggingElement).data('pieceId') == $(this).data('correctId')) {
return true;
}
return false;
},
drop:function(e){
// console.log(e) to find the toElement refers to the dragging element.
var draggingElement = e.toElement;
$(draggingElement).remove();
// remove the covered opacity
$(this).addClass('invisible');
// save the game once
gameStorage.save();
// count how many dropped
game.completedCount += 1;
if (game.completedCount >= game.pieces.length) {
gameController.gotoEndGame();
// clear game, completed.
}
}
});
idCount += 1;
}
}
},
renderStartArea: function() {
// please ensure called createPiece before rendering.
// clear all
$('.piece').remove();
var rowsCount = gameSetting.rowsOptions[game.level];
var pieceWidth = gameSetting.areaWidth / rowsCount;
for (var i in game.pieces) {
var data = game.pieces[i];
// dimension
var element = $("<div class='piece'>");
element.css('width', pieceWidth + 'px');
element.css('height', pieceWidth + 'px');
// image
element.css('background-image', 'url(' + data.imgSrc + ')');
element.addClass('piece');
element.data('pieceId', data.pieceId);
// add to #game, not #start-area.
// #start-area 只係視覺效果。
$('#game').append(element);
// position
element.css('left', data.left);
element.css('top', data.top);
// dragging
element.draggable({
revert: "invalid",
start: function(){
$(this).addClass('active');
},
stop: function(){
$(this).removeClass('active');
}
});
}
},
renderDestArea: function() {
$('#dest-area').css("background-image", "url("+imageDroppingView.droppedFile+")");
}
}
var imageDroppingView = {
dropZone: $('#dropzone')[0],
droppedFile: undefined,
init: function() {
// "this" refers to imageDroppingView.
this.dropZone.ondragover = function(){
// "this" refers to dropzone
$(this).addClass("active");
return false;
};
this.dropZone.ondragout = function() {
$(this).removeClass("active");
return false;
};
this.dropZone.ondragend = function() {
$(this).removeClass("active");
return false;
};
this.dropZone.ondrop = function(e) {
$(this).removeClass("active");
// "this" refers to dropzone, we need to use
// imageDroppingView.droppedFile to access that variable.
var file = e.dataTransfer.files[0];
var reader = new FileReader();
// used "e" before, so use "event" here:
reader.onload = function(event){
imageDroppingView.droppedFile = event.target.result;
// make the final image fits the cropping requirement.
cropper.makeImage(imageDroppingView.droppedFile);
gameController.createPiecesData();
// little preview image.
imageDroppingView.renderPreview();
};
reader.readAsDataURL(file);
return false;
};
},
renderPreview: function() {
$('#preview-img').css("background-image", "url("+imageDroppingView.droppedFile+")");
}
};
// view interaction
$('#start-game').on('click', function(){
gameController.startGame();
});
$('#game').delegate('.piece', 'click', function(){
$('.piece').removeClass('selected');
$(this).addClass('selected');
});
$(window).on('keydown', function(e){
if (e.keyCode == 37) {
// game.
}
});
// controller
var gameController = {
gotoPreGame: function() {
imageDroppingView.init();
},
startGame: function() {
// try loading previous game
var previousGame = gameStorage.load();
// if (previousGame !== undefined) {
// console.log("loaded game.");
// game = previousGame;
// } else {
// new game, randomize
this.randomizePiecesData();
// }
// destination hidden grid
gameView.createDestDropGrid();
// render view
gameView.render();
},
gotoEndGame: function() {
$('#end-game').addClass('show');
},
createPiecesData: function() {
// empyt the pieces
game.pieces.length = 0;
var rowsCount = gameSetting.rowsOptions[game.level];
var pieceWidth = gameSetting.areaWidth / rowsCount;
var idCount = 1; // begins with 1.
for (i = 0; i < rowsCount; i++) {
for (j = 0; j < rowsCount; j++) {
// image
var imgSrc = cropper.cropImage(i * pieceWidth, j * pieceWidth, pieceWidth, pieceWidth);
game.pieces.push({
pieceId: idCount,
order: idCount,
left: i * pieceWidth,
top: j * pieceWidth,
rotation: 0,
imgSrc: imgSrc
});
idCount += 1;
}
}
},
randomizePiecesData: function() {
// randomize position
game.pieces.sort(function(){
// return > 0 and < 0 randomly
return Math.random() - 0.5;
});
// randomize rotation
for (var i in game.pieces) {
var data = game.pieces[i];
var rotationIndex = Math.floor(Math.random() * 4); // 0, 1, 2, 3
data.rotation = gameSetting.rotations[rotationIndex];
data.order = i;
}
// randomize position
// 偷雞:
for (var i = 0; i<10; i++) {
var randomA = Math.floor(Math.random()*game.pieces.length);
var randomB = Math.floor(Math.random()*game.pieces.length);
var left = game.pieces[randomA].left;
var top = game.pieces[randomA].top;
game.pieces[randomA].left = game.pieces[randomB].left;
game.pieces[randomA].top = game.pieces[randomB].top;
game.pieces[randomB].left = left;
game.pieces[randomB].top = top;
}
}
}
// entry point
gameController.gotoPreGame();
<!DOCTYPE html>
<html>
<head>
<title>Testing Client A</title>
<style>
*{box-sizing:border-box;}
.hidden {display: none;}
#game {
overflow: auto;
width: 600px;
position: relative;
}
.area {
width: 300px;
height: 300px;
border: 1px solid gray;
position: relative;
}
#start-area {
float: left;
}
#dest-area {
float: right;
background-size: cover;
background-position: center center;
}
.invisible{
opacity: 0 !important;
transition: all 0.3s ease-out;
}
/* end game */
#end-game {
position: absolute;
width: 100vw;
height: 100vh;
background: blue;
top: -9999px;
left: 0;
}
#end-game.show {
top: 0;
transition: all 0.3s ease-out;
}
/* pieces */
.dest-piece,
.piece {
/*background: gray;*/
position: absolute;
left: 0;
top: 0;
z-index: 1;
}
.dest-piece{
background: white;
opacity: 0.8;
}
.piece.active {
z-index: 999;
transform: scale(1.1);
box-shadow: 0 5px 3px rgba(0,0,0,.5);
}
.piece.selected {
border: 1px dashed black;
}
/* preview */
#preview {
overflow: auto;
width: 350px;
}
#preview-img {
width: 50px;
height: 50px;
float: left;
background-size: cover;
}
#dropzone {
width: 300px;
height: 50px;
border: 3px dashed gray;
float:right;
}
#dropzone.active {
border-color: black;
}
</style>
</head>
<body>
<div id="game">
<!-- Don't use start-area it place pieces -->
<div id="start-area" class="area"></div>
<div id="dest-area" class="area"></div>
<canvas id='cropper' class="hidden"></canvas>
<img id="game-image" class="hidden">
</div>
<div id="preview">
<div id="preview-img"></div>
<div id="dropzone"></div>
</div>
<div id="end-game">
You Won!
</div>
<button id="start-game">Start Game</button>
<script src='jquery-2.1.1.min.js'></script>
<script src='jquery-ui/jquery-ui.min.js'></script>
<script src='game.js'></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment