Last active
August 29, 2015 14:27
-
-
Save makzan/0f8d9937154f341b3257 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
https://www.dropbox.com/s/nmh10tmsdkto1u1/Testing%20Client%20A.zip?dl=0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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