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