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();

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/8685.html

(0)
上一篇 2021年7月18日
下一篇 2021年7月18日

相关推荐

发表回复

登录后才能评论