JavaScript任意數量的拼圖游戲
(function($) {
var puzzleConfig = {
sizeX: 3,
sizeY: 3
};
//全局常量
var Constants={
//每一片拼圖透明度較低時候的透明度值
fadeOpacity: 0.8,
//放拼圖元素的水平方向padding+border的合計值,用于載入拼圖后控制容器尺寸
puzzleContainerExtra: 42
};
//圖片相關變量
var puzzleImage=null,
imageURL="",
//圖片上傳標識,為true時表示相關設置合理,選擇圖片后將進入游戲
checkFlag=false,
imageWidth=0,
imageHeight=0;
//拼圖相關變量
var puzzleWidth=0,
puzzleHeight=0,
puzzleItemWidth=0,
puzzleItemHeight=0,
puzzleSizeX=0,
puzzleSizeY=0,
//拼圖數目
puzzleNumber=0,
//計數器,計算從開始到完成游戲用的步數
moveStepCount=0,
//拼圖步數以及是否完成的提示文字
puzzleNote=null,
//保存每一片拼圖的正確的坐標值的數組
validPosArrayX=[],
validPosArrayY=[],
//保存每一片拼圖的數組,索引順序和正確的拼圖順序相同
puzzleArray = [],
//整個拼圖元素本身
puzzle=null,
//最終放置該拼圖的父元素節點
puzzleSetElem=null;
//初始第一步,讀取拼圖設置和圖片源,包括對填寫內容的驗證*/
var puzzleConfigSet = function() {
//類名常量
var sizeInputClassName = "size_input",
noteWarnClassName = "note_warn",
currentProgressClassName = "current_progress",
validImageSuffix = ".jpg|.jpeg|.gif|.bmp|.png";
//放置拼圖的由外層變量保存的元素
puzzleSetElem=$ ("puzzleSet");
//取得對應元素
var sizeXElem = $("sizeX"),
sizeYElem = $("sizeY"),
sizeSetNote = $("sizeSetNote"),
uploadBtn = $("uploadBtn"),
fileImage = $("fileImage"),
uploadProgress = $("uploadProgress"),
currentProgress = uploadProgress.getFirst("." + currentProgressClassName),
uploadNote = $("uploadNote");
//拼圖尺寸設定檢查
var puzzleSizeCheck = function() {
var sizeX = sizeXElem.value,
sizeY = sizeYElem.value,
numberReg = /^\d{1,2}$/;
if (numberReg.test(sizeX) && numberReg.test(sizeY)) {
if (sizeX >= 2 && sizeX <= 10 && sizeY >= 2 && sizeY <= 10) {
puzzleConfig.sizeX = sizeX;
puzzleConfig.sizeY = sizeY;
checkFlag = true;
} else {
sizeSetNote.addClass(noteWarnClassName);
}
} else {
sizeSetNote.addClass(noteWarnClassName);
}
};
//圖片尺寸檢查
var imageCheck = function(image) {
var minWidth = 30,
maxWidth = 850,
minHeight = 30;
if (image.width >= 30 && image.width <= 850 && image.height > 30) {
checkFlag = checkFlag && true;
} else {
uploadNote.addClass(noteWarnClassName);
checkFlag = false;
}
};
//圖片格式檢查
var formatCheck = function(image) {
var fileURL = fileImage.value.toLowerCase();
//獲取文件拓展名
formatSuffix = fileURL.substring(fileURL.lastIndexOf("."));
if (formatSuffix&&validImageSuffix.contains(formatSuffix)) {
//如果是正確格式的圖片文件
checkFlag = checkFlag && true;
} else {
alert("請上傳正確格式的圖片文件(" + validImageSuffix + ")");
checkFlag = false;
}
};
//拼圖尺寸輸入框的事件
$$("." + sizeInputClassName).addEvent("focus", function() {
sizeSetNote.removeClass(noteWarnClassName);
});
//讀取選擇上傳的圖片
puzzleImage = new Image();
puzzleImage.onload = function() {
imageCheck(puzzleImage);
if (checkFlag) {
imageWidth = puzzleImage.width;
//由于圖片尺寸不一定能被拼圖尺寸整除,因此做邊緣裁剪
while(imageWidth % puzzleConfig.sizeX != 0){
imageWidth--;
}
imageHeight = puzzleImage.height;
while(imageHeight % puzzleConfig.sizeY != 0){
imageHeight--;
}
imageURL= puzzleImage.src;
puzzleSetElem.empty();
var containerWidth = imageWidth+Constants.puzzleContainerExtra,
properContainerWidth = containerWidth>120?containerWidth:120;
puzzleSetElem.getParent().setStyles({
width: properContainerWidth
});
createPuzzle(); //創建拼圖
}
else{
//如果讀取后圖片尺寸不合適的話,重置圖片上傳
uploadProgress.style.display = "none";
currentProgress.setStyle("width", 0);
uploadBtn.style.display = "";
}
};
if (typeof FileReader == "undefined") {
//如果是不支持File API的瀏覽器
fileImage.onchange = function() {
puzzleSizeCheck();
if (checkFlag) {
formatCheck();
}
if (checkFlag) {
puzzleImage.src = fileImage.value;
}
};
} else {
//如果支持File API,可以顯示讀取進度條
var imageReader = new FileReader();
//對象URL(blob URL),經測試新版Chrome也支持window.URL
function createObjectURL(blob){
if(window.URL){
return window.URL.createObjectURL(blob);
}else if(window.webkitURL){
return window.webkitURL.createObjectURL(blob);
}else{
return null;
}
}
//開始讀取
imageReader.onloadstart = function() {
puzzleSizeCheck();
if(checkFlag){
formatCheck();
}
if (checkFlag) {
uploadBtn.style.display = "none";
uploadProgress.style.display = "";
}
};
//讀取中
imageReader.onprogress = function(event) {
if (checkFlag) {
var percentage = 100 * parseInt(event.loaded / event.total) + "%";
currentProgress.setStyle("width", percentage);
}
};
imageReader.onload = function(event) {
if (checkFlag) {
//IE10也支持blob URL
var url=createObjectURL(fileImage.files[0]);
puzzleImage.src = url;
}
};
fileImage.onchange = function() {
imageReader.readAsDataURL(fileImage.files[0]);
};
}
};
//用于創建拼圖
var createPuzzle = function() {
//classNameSet表示生成的元素的class名
var classNameSet = {
listContainer: "puzzle_container",
list: "puzzle_list",
item: "puzzle_item"
};
//各類元素對應的基本樣式
var puzzleStyle = {
listContainer: {
position: "relative",
width: imageWidth,
height: imageHeight,
margin: "0 auto"
},
list: {
},
item: {
position: "absolute"
}
};
//計算得到每一塊拼圖的尺寸
puzzleSizeX = puzzleConfig.sizeX;
puzzleSizeY = puzzleConfig.sizeY;
puzzleWidth = imageWidth;
puzzleHeight = imageHeight;
puzzleItemWidth = puzzleWidth / puzzleSizeX;
puzzleItemHeight = puzzleHeight / puzzleSizeY;
puzzleNumber = puzzleSizeX * puzzleSizeY;
//建立一個臨時數組,用于生成隨機順序的拼圖塊
var randomOrderPuzzleArray=[];
//創建元素
puzzle = elementsCreate();
showAnime();
//創建整個拼圖的dom,返回最外層的父級元素
function elementsCreate() {
var listContainer = new Element("div");
listContainer.addClass(classNameSet.listContainer);
listContainer.setStyles(puzzleStyle.listContainer);
var list = new Element("ul");
list.addClass(classNameSet.list);
list.setStyles(puzzleStyle.list);
//先通過循環,創建每一個拼圖塊,并按正確順序存入數組
for(var i = 0, len = puzzleNumber; i < len; i++) {
var item = new Element("li");
//為每塊拼圖保存自身的正確索引
var indexSet = i + 1;
item.store("puzzleIndex", indexSet);
item.addClass(classNameSet.item);
//增加基本樣式
item.setStyles(puzzleStyle.item);
//以正確順序保存每一個拼圖塊到數組
puzzleArray.push(item);
}
//建立一個正確順序數組的副本
var puzzleArrayClone=puzzleArray.clone();
//再次通過循環,創建一個亂序的拼圖數組,并把這個數組顯示到頁面中
for (i = 0, len = puzzleNumber; i < len; i++) {
var randomItem = puzzleArrayClone.getRandom();
//為避免重復,需要把被取出來的元素在副本數組中刪除
puzzleArrayClone.erase(randomItem);
//為每一塊取出來的元素設置可變的位置索引
var posIndex = i + 1;
randomItem.posIndex = posIndex;
//獲取取出來的元素的正確索引,用于接下來計算拼圖背景圖位置
var correctIndex = randomItem.retrieve("puzzleIndex");
//計算位置
var topSet = Math.floor((posIndex - 1) / puzzleSizeX) * puzzleItemHeight,
leftSet = (posIndex - 1) % puzzleSizeX * puzzleItemWidth,
//計算符合正確索引的背景圖位置
backgroundSetX = -(correctIndex - 1) % puzzleSizeX * puzzleItemWidth,
backgroundSetY = -(Math.floor((correctIndex - 1) / puzzleSizeX) * puzzleItemHeight),
backgroundString = "url(" + imageURL + ") " + backgroundSetX + "px " + backgroundSetY + "px " + "no-repeat";
//添加關鍵樣式
randomItem.setStyles({
width: Math.ceil(puzzleItemWidth),
height: Math.ceil(puzzleItemHeight),
background: backgroundString,
left: leftSet,
top: topSet,
zIndex: posIndex
});
//生成合理的位置坐標數組
validPosArrayX.push(leftSet);
validPosArrayY.push(topSet);
//存放亂序元素到亂序數組
randomOrderPuzzleArray.push(randomItem);
}
//組合拼圖的各個元素
list.adopt(randomOrderPuzzleArray);
listContainer.adopt(list);
return listContainer;
}
//為拼圖的初始化創建動畫
function showAnime(){
//一些動畫參數
var timeSpace=50,
//垂直移動的間距
distance=30,
//計數用
count=0,
timeFlag;
//所有拼圖先隱藏,透明度置為0
for(var i=0,len=puzzleArray.length;i<len;i++){
puzzleArray[i].setStyle("opacity",0);
}
//更新到頁面dom中,準備開始動畫
puzzleSetElem.grab(puzzle);
var enterFrameHandler=function(){
var puzzleItem=randomOrderPuzzleArray[count++];
var endTop=parseInt(puzzleItem.getStyle("top"));
var startTop=endTop-distance;
puzzleItem.set("morph",{
transition: Fx.Transitions.Quad.easeOut
});
puzzleItem.morph({
top:[startTop,endTop],
opacity:Constants.fadeOpacity
});
if(count<puzzleNumber){
//對最后一個拼圖塊的動畫結束做偵聽
if(count==puzzleNumber-1){
var lastMorph=puzzleItem.get("morph");
var showAnimeEnd=function(){
lastMorph.removeEvent("complete",showAnimeEnd);
puzzleEventBind();
}
lastMorph.addEvent("complete",showAnimeEnd);
}
timeFlag=setTimeout(enterFrameHandler,timeSpace);
}
};
timeFlag=setTimeout(enterFrameHandler,timeSpace);
}
};
//拼圖的相關事件綁定,也是游戲的核心控制邏輯
var puzzleEventBind=function(){
//拼圖游戲控制相關的變量
var selectedItem=null,
//當前選中的拼圖位置索引
selectedIndex=0,
//用于保存當前鼠標正在拖動的拼圖的zIndex值
selectedItemZIndex=0,
//每一次切換拼圖位置的時候,都涉及到2塊拼圖,鼠標拖動的這塊和交換位置的另外一塊,這個就是另外一塊
relatedItem=null,
//依照鼠標當前的位置,判斷得到的目標索引,如果鼠標此時放開,就是說把選中的拼圖移到現在鼠標所在的位置
targetIndexNew=0,
//通過new和old來區分鼠標從一個目標索引更換到另一個目標索引
targetIndexOld=0,
//判斷是否進行一次拼圖位置移動的邏輯值,只有當目標索引值有改變時,才允許進行拼圖位置移動
isTargetIndexChanged=false,
//判斷鼠標指針是否在拼圖的區域之內
isInsidePuzzle=false,
//鼠標點擊拼圖的某一個點的時候,距離拼圖的左上角定位點有的距離值
disX=0,
disY=0;
//計算獲取整個拼圖的左上角點的坐標
var puzzlePos=puzzle.getPosition();
var puzzlePosX=puzzlePos.x,
puzzlePosY=puzzlePos.y;
//重新設置每一個元素的動畫速度
(function(){
for(var i=0,len=puzzleArray.length;i<len;i++){
var puzzleItem=puzzleArray[i];
puzzleItem.set("morph",{
duration:250
});
}
})();
//計數函數準備
var updateCount = (function(){
var stepCount = $("stepCount");
puzzleNote = stepCount.getParent();
return function(){
stepCount.set("text", moveStepCount);
};
})();
//添加事件
puzzle.addEvent("mouseover",mouseOverHandler);
puzzle.addEvent("mouseout",mouseOutHandler);
puzzle.addEvent("mousedown",mouseDownHandler);
puzzle.addEvent("mouseup",mouseUpHandler);
//鼠標經過
function mouseOverHandler(event){
var target=event.target;
if(puzzleArray.contains(target)){
target.setStyle("opacity",1);
}
}
//鼠標移出
function mouseOutHandler(event){
var target=event.target;
if(puzzleArray.contains(target)){
target.setStyle("opacity",Constants.fadeOpacity);
}
}
//鼠標按下
function mouseDownHandler(event){
var target=event.target;
//race("[mouseDownHandler]selectedItem ="+selectedItem);
//如果當前沒有其他目標選中,且鼠標選中的目標是拼圖塊
if(!selectedItem&&puzzleArray.contains(target)){
if(target.getStyle("opacity")<1){
target.setStyle("opacity",1);
}
//設置當前選中的目標及索引
selectedItemZIndex=target.getStyle("zIndex");
target.setStyle("zIndex",5000);
selectedItem=target;
selectedIndex=target.posIndex;
//設置初始目標索引
targetIndexNew=targetIndexOld=selectedIndex;
//計算出鼠標點擊的點和拼圖左上角定位點的偏差距離
var targetPos=target.getPosition();
disX=event.page.x-targetPos.x;
disY=event.page.y-targetPos.y;
//增加鼠標移動的事件偵聽,讓拼圖塊跟隨鼠標移動,并判斷當前位置
document.addEvent("mousemove",mouseMoveHandler);
}
}
//鼠標松開
function mouseUpHandler(event){
//如果有元素處于拖動狀態,取消
if(selectedItem){
selectedItem.setStyle("opacity",Constants.fadeOpacity);
selectedItem.setStyle("zIndex",selectedItemZIndex);
document.removeEvent("mousemove",mouseMoveHandler);
//松開之后,根據目標索引和拖動元素的索引,移動拼圖,并更新dom結構
if(isInsidePuzzle){
//如果目標索引是一塊別的拼圖
if(targetIndexNew!=selectedIndex){
puzzleItemMove(selectedItem,targetIndexNew,puzzleItemSwitch);
}else{
//還原回原來的位置
puzzleItemMove(selectedItem,selectedIndex);
selectedItem=null;
relatedItem=null;
}
}else{
//如果鼠標在拼圖之外的區域松開,則被拖動的拼圖還原回原來的位置
puzzleItemMove(selectedItem,selectedIndex);
selectedItem=null;
relatedItem=null;
targetIndexNew = targetIndexOld = selectedIndex;
}
}
}
//鼠標移動
function mouseMoveHandler(event){
var mouseX=event.page.x,
mouseY=event.page.y;
event.preventDefault();
//設置選中元素的位置,跟隨鼠標
selectedItem.setPosition({
x:mouseX-disX-puzzlePosX,
y:mouseY-disY-puzzlePosY
})
//計算鼠標當前位置是否在拼圖區域之內(拼圖邊緣也算在外)
isInsidePuzzle=(function(){
if(mouseX<=puzzlePosX||mouseX-puzzlePosX>=puzzleWidth){
return false;
}
if(mouseY<=puzzlePosY||mouseY-puzzlePosY>=puzzleHeight){
return false;
}
return true;
})();
//如果鼠標當前位置在拼圖區域之內,再做目標索引計算
if(isInsidePuzzle){
//race("[mouseMoveHandler]isInsidePuzzle = true");
//計算目標索引,xIndex和yIndex分別表示當前位置所處的列序號和行序號
var xIndex=Math.ceil((mouseX-puzzlePosX)/puzzleItemWidth),
yIndex=Math.ceil((mouseY-puzzlePosY)/puzzleItemHeight);
targetIndexNew=(yIndex-1)*puzzleSizeX+xIndex;
if(targetIndexNew!=targetIndexOld){
isTargetIndexChanged=true;
}
//只有當目標索引發生改變時,才移動拼圖做示意
if(isTargetIndexChanged){
//如果上一個目標索引的拼圖不是鼠標正在移動的這個,那么就需要恢復這張拼圖的位置到它原來的地方
if(targetIndexOld!=selectedIndex){
var lastRelatedItemIndex=relatedItem.posIndex;
puzzleItemMove(relatedItem,lastRelatedItemIndex);
}
//更新相關元素,取得拼圖數組中posIndex等于當前的目標索引的元素
relatedItem=puzzleArray.filter(function(item, index){
return item.posIndex == targetIndexNew;
})[0];
//如果下一個目標索引,不是被拖走的拼圖原來所在的位置,就移動新的目標索引的拼圖到被拖走的拼圖的位置
if(targetIndexNew!=selectedIndex){
puzzleItemMove(relatedItem,selectedIndex);
}
//重置目標索引改變的邏輯值
isTargetIndexChanged=false;
//更新上一個目標索引
targetIndexOld=targetIndexNew;
}
}else{
//如果移到拼圖區域之外,則考慮還原上一個目標索引的拼圖
if(targetIndexOld!=selectedIndex){
var lastRelatedItemIndex=relatedItem.posIndex;
puzzleItemMove(relatedItem,lastRelatedItemIndex);
}
//還原targetIndexOld的值,以處理移到拼圖外的情況。
targetIndexOld = selectedIndex;
}
}
//每一次拼圖交換的功能實現的函數,更改對應元素的posIndex,并更改zIndex
function puzzleItemSwitch(){
//交換元素的posIndex
selectedItem.posIndex=targetIndexNew;
relatedItem.posIndex=selectedIndex;
//交換元素的zIndex,通過posIndex來賦值
selectedItem.setStyle("zIndex",selectedItem.posIndex);
relatedItem.setStyle("zIndex",relatedItem.posIndex);
//清除對相關元素的引用
selectedItem=null;
relatedItem=null;
//一次更換完成,計數器+1
moveStepCount++;
updateCount();
//然后再判斷拼圖游戲是否完成
clearJudgement();
}
//每一塊拼圖在游戲中的移動函數
function puzzleItemMove(moveItem,moveToIndex,endFn){
var moveToX=validPosArrayX[moveToIndex-1],
moveToY=validPosArrayY[moveToIndex-1],
originZIndex=moveItem.posIndex;
moveItemMorph=moveItem.get("morph");
moveItemMorph.addEvent("start",moveStartHandler);
moveItemMorph.addEvent("complete",moveEndHandler);
moveItem.morph({
left:moveToX,
top:moveToY
});
function moveStartHandler(){
moveItem.setStyle("zIndex",1000);
}
function moveEndHandler(){
moveItemMorph.removeEvent("start",moveStartHandler);
moveItemMorph.removeEvent("complete",moveEndHandler);
moveItem.setStyle("zIndex",originZIndex);
//結尾執行的函數,如果需要的話
if(typeOf(endFn)=="function"){
endFn();
}
}
}
//完成拼圖游戲的判定函數
function clearJudgement(){
//檢查puzzleArray中的每一個元素的puzzleIndex和posIndex是否全部一致
var isGameClear=puzzleArray.every(function(item, index){
var puzzleIndex=item.retrieve("puzzleIndex");
return item.posIndex==puzzleIndex;
});
if(isGameClear){
clearShow();
}
}
//確定完成拼圖游戲后,執行的函數
function clearShow(){
//清除所有事件偵聽
puzzle.removeEvent("mouseover",mouseOverHandler);
puzzle.removeEvent("mouseout",mouseOutHandler);
puzzle.removeEvent("mousedown",mouseDownHandler);
puzzle.removeEvent("mouseup",mouseUpHandler);
var clearAnimeFlag=null,
count=0;
//按順序點亮所有拼圖的動畫
var enterFrameHandler=function(){
var item=puzzleArray[count++];
item.fade(1);
if(count<puzzleNumber){
clearAnimeFlag=setTimeout(enterFrameHandler,50);
}
};
clearAnimeFlag=setTimeout(enterFrameHandler,50);
//游戲完成后的信息~?
puzzleNote.set('html','Congratulations ! Your final step count is <em class="step_count">'+moveStepCount+'</em>.');
}
}
//創建全局變量puzzleGame
window.puzzleGame={};
//添加方法到全局變量puzzleGame中
puzzleGame.start = function() {
puzzleConfigSet();
};
})(document.id);
puzzleGame.start();</pre>
本文由用戶 程序猿123 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!