貌似最近腾讯手机游戏天天爱消除挺火的,我也是粉丝之一,最近对javascript一直比较感兴趣然后想用js仿造一个,应该不是太难,本文系边写代码别写博客,详细叙述开放过程,如果最后开发成功就发表,如果最后失败了就删除草稿。废话不多说开始:
写完后重新声明以下均为本人边写边记录思路开发过程,中途走了很多大弯路小弯路。如不想被误导可直接研究最后的源代码
qqqun.21.777.12
function createDiv(iTop){//构造div
var oDiv = document.createElement("div");
oDiv.style.backgroundImage="url(img/zImg_"+Math.round(Math.random()*6)+".jpg)";
oDiv.style.top = iTop + "px";
oDiv.style.MozUserSelect="none"
var posX;
var posY;
oDiv.οnmοusedοwn=function(e){//鼠标按下时
e = e ? e : (window.event ? window.event : null);
posX = e.clientX - this.offsetLeft;//先记录按下时与鼠标位置的差值后面移动时再做差值
posY = e.clientY - this.offsetTop;
moveable = true;
this.style.zIndex = 100;//zindex默认为空,不设置个数的话拖动过程会被其他元素覆盖掉
iFlag = 1;
};
oDiv.οnmοusemοve=function(e){//鼠标拖动时
e = e ? e : (window.event ? window.event : null);
if(moveable){
this.style.left = (e.clientX - posX) + "px";
this.style.top = (e.clientY - posY) + "px";
}
};
oDiv.οnmοuseup=function (e){//鼠标松起时
if(moveable) {
moveable = false;
}
this.style.zIndex = "";
};
return oDiv;
}
var moveable = false;
function createDiv(iTop){//构造div
var oDiv = document.createElement("div");
oDiv.style.backgroundImage="url(img/zImg_"+Math.round(Math.random()*6)+".jpg)";
oDiv.style.top = iTop + "px";
oDiv.style.MozUserSelect="none"
var posX,posY;
var posEx,posEy;
oDiv.οnmοusedοwn=function(e){
e = e ? e : (window.event ? window.event : null);
posEx = e.clientX;//按下鼠标时同时记住鼠标位置
posEy = e.clientY;
posX = e.clientX - oDiv.offsetLeft;
posY = e.clientY - oDiv.offsetTop;
moveable = true;
this.style.zIndex = 100;
};
oDiv.οnmοusemοve=function(e){
e = e ? e : (window.event ? window.event : null);
if(moveable){
if(Math.abs(e.clientX - posEx)>Math.abs(e.clientY - posEy))//移动前先比较鼠标新位置与旧位置,即位置方向再去改变,这样如果是向左,则左差值越来越大就会一直像左,同理上下
oDiv.style.left = (e.clientX - posX) + "px";
else
oDiv.style.top = (e.clientY - posY) + "px";
}
};
oDiv.οnmοuseup=function (e){
if(moveable) {
moveable = false;
}
this.style.zIndex = "";
};
return oDiv;
}
这样拖动算算解决了冰山一角,还要解决拖动时向哪个方向走,对方的div要顺势向这边移动。有个交换的效果。
function findIndex(obj){
for(var i=0,m=arrAll[iArrIndex].length;i
function createDiv(iTop,iArr){//加在了这
var oDiv = document.createElement("div");
oDiv.style.backgroundImage="url(img/zImg_"+Math.round(Math.random()*6)+".jpg)";
oDiv.style.top = iTop + "px";
oDiv.style.MozUserSelect="none"
var posX,posY;
var posEx,posEy;
oDiv.οnmοusedοwn=function(e){
e = e ? e : (window.event ? window.event : null);
posEx = e.clientX;
posEy = e.clientY;
posX = e.clientX - oDiv.offsetLeft;
posY = e.clientY - oDiv.offsetTop;
moveable = true;
this.style.zIndex = 100;
iArrIndex = iArr;//加在了这,没此点击动态改变数组的指向
};
同时加载模型时肯定是这样写了
for(var i=0;i<7;i++){
for(var j=0;j<7;j++){
var oDiv = createDiv(j*49,i);
oCons[i].appendChild(oDiv);
arrAll[i][j] = oDiv;
}
}
有人想,既然i(区分数组的标识)可以传递进去,干脆把j(区分位置的标识)也传递进去得了,省得还得遍历麻烦,这里开始我也是这么想,但考虑消除后数组里的位置j会递增或递减的不好控制,所以还是遍历j比较省事。而我们构造时i就不用考虑那么多,哪排数组消除了元素构造哪排的i,i值不会随便改。我们可以在onmouseup里写一个alert(findIndex(this));测试下返回值是否正确。本人已测。
function findDiv(iArr,jArr){
return arrAll[iArr][jArr];
}
然后我们鼠标 点击可以获取到div(this)又可以得到他的相邻元素。接下来要解决的就是相互滑动了。这个其实也不难,我们看鼠标的移动方向来判断4个方向哪个div互换,哪个div做反向移动就行了。
做到这里时我突然发现其实我们的移动都是整步移动,每次就移动一个宽度或一个高度,所以上面的还得先改动下:
oDiv.style.left = (e.clientX - posX) + "px"
直接改为left直接加减49。但这样就没有拖动效果了,直接瞬移,所以onmousemove处的不能改,只能加个位移长度的控制49,简单加个
function findIndex(obj){
for(var i=0;i<7;i++){
for(var j=0;j<7;j++){
if(obj===arrAll[i][j]){
return {ri:i,rj:j};
}
}
}
};
然后我们要单独写一个交换位置的函数,这个函数不仅要交换数组的位置,还要交换页面上div元素的位置,数组的位置好说如下:
function changeIndex(oIdiv,oLdiv){
var indexIJ_I = findIndex(oIdiv);
var indexIJ_L = findIndex(oLdiv);
arrAll[indexIJ_L.ri][indexIJ_L.rj] = oIdiv;
arrAll[indexIJ_I.ri][indexIJ_I.rj] = oLdiv;
}
然后交换div位置,有人说直接取oIdiv,oLdiv的left,top互换不就行了,但这里做到这一步时我们的left,top已经被鼠标拖动了,是没法再用的,我们做交换div位置的目的是放置好left,和top为49的整数倍。但我们实质改变div的位置还必须要用left,top去改变,难道要传参过来原位置、?
后来我想了半天7个数组是没错的,但7个长条div就有点死板多余了,我们完全可以去掉这7个长div,没啥用了,当初float:bottom想法作废的时候就该把它作废了。然后加载模型要改,横向纵向都是49的倍数,那交换位置好说了,得到了数组里的位置去乘以49就知道原位置top,left了。
qqqun.21.777.12
居然走了个大弯路。其它的函数不用变,继续说刚才的交换页面的div位置,我们刚才已经有思路了,利用数组位置获取原div的left,top。更换位置的参数变为:
function changeIndex(oIdiv,oLdiv){
var indexIJ_I = findIndex(oIdiv);
var indexIJ_L = findIndex(oLdiv);
oIdiv.style.left = indexIJ_L.ri*49;//临div的原位置给原div
oIdiv.style.top = indexIJ_L.rj*49;
oLdiv.style.left = indexIJ_I.ri*49;//原div的原位置给临div
oLdiv.style.top = indexIJ_I.rj*49;
arrAll[indexIJ_L.ri][indexIJ_L.rj] = oIdiv;
arrAll[indexIJ_I.ri][indexIJ_I.rj] = oLdiv;
}
突然觉得用7个数组实在是太明智了!折腾一圈后最终代码如下:
qqqun.21.777.12
这时的代码已然是一个可拖动换位置的模型了,其中
border:1px solid red是为了没有本地图片时区分各个div,如果有图片可以去掉此句。
小有成就,然后就是加入逻辑关系,有消除机会才换位置,没有则不换。这里说的消除机会,无非就是比较互换后的两个div,新的位置是否有机会消除,注意这里是两个div,线看一个。
function ifCanDelete(obj){//查看某个div的当前位置是否可消除,返回的是可消除的div数组
var colorI = obj.style.backgroundImage;
var ifx = findIndex(obj).ri;
var ify = findIndex(obj).rj;
var arrLeft = new Array();
var arrTop = new Array();
if(ifx-1>=0){//取左侧第一个
var l1 = findColor(ifx-1,ify);
if(ifx-2>=0&&l1==colorI){//如果左侧第一个相同再取第二个,当然不过能边界0
arrLeft[arrLeft.length] = arrAll[ifx-1][ify];
var l2 = findColor(ifx-2,ify);
if(l1==l2)
arrLeft[arrLeft.length] = arrAll[ifx-2][ify];
}
}
if(ifx+1<7){//右侧同理
var r1 = findColor(ifx+1,ify)
if(ifx+2<7&&r1==colorI){
arrLeft[arrLeft.length] = arrAll[ifx+1][ify];
var r2 = findColor(ifx+2,ify);
if(r1==r2)
arrLeft[arrLeft.length] = arrAll[ifx+2][ify];
}
}
if(ify-1>=0){//上侧
var t1 = findColor(ifx,ify-1);
if(ify-2>=0&&t1==colorI){
arrTop[arrTop.length] = arrAll[ifx][ify-1];
var t2 = findColor(ifx,ify-2);
if(t1==t2)
arrTop[arrTop.length] = arrAll[ifx][ify-2];
}
}
if(ify+1<7){//下侧
var b1 = findColor(ifx,ify+1);
if(ify+2<7&&b1==colorI){
arrTop[arrTop.length] = arrAll[ifx][ify+1];
var b2 = findColor(ifx,ify+2);
if(b1==b2)
arrTop[arrTop.length] = arrAll[ifx][ify+2];
}
}
if(arrLeft.length>1&&arrTop.length>1){
for(var k=0;k1||arrTop.length>1){
if(arrLeft.length>1){
arrLeft[arrLeft.length] = obj;
return arrLeft;
}else{
arrTop[arrTop.length] = obj;
return arrTop;
}
}else{
return [];
}
}
function findColor(ix,iy){//按位置得到背景色
return arrAll[ix][iy].style.backgroundImage;
}
相应用到这个函数在交互位置的函数里,如下
function changeIndex(oIdiv,oLdiv){
var indexIJ_I = findIndex(oIdiv);
var indexIJ_L = findIndex(oLdiv);
arrAll[indexIJ_L.ri][indexIJ_L.rj] = oIdiv;//更改数组位置
arrAll[indexIJ_I.ri][indexIJ_I.rj] = oLdiv;
var arryI = ifCanDelete(oIdiv);
var arryL = ifCanDelete(oLdiv);
if(arryI.length==0&&arryL.length==0){//如果没有消除机会返回位置
oIdiv.style.left = indexIJ_I.ri*49;//回原位置
oIdiv.style.top = indexIJ_I.rj*49;
oLdiv.style.left = indexIJ_L.ri*49;//v
oLdiv.style.top = indexIJ_L.rj*49;
arrAll[indexIJ_L.ri][indexIJ_L.rj] = oLdiv;
arrAll[indexIJ_I.ri][indexIJ_I.rj] = oIdiv;
}else{
oIdiv.style.left = indexIJ_L.ri*49;//临div的原位置给原div
oIdiv.style.top = indexIJ_L.rj*49;
oLdiv.style.left = indexIJ_I.ri*49;//原div的原位置给临div
oLdiv.style.top = indexIJ_I.rj*49;
}
oIdiv.style.backgroundImage;
}
问题解决了,然后是消除了,这里已经有了返回的div数组,就去删除这些div就行了,同时数组里也要删除掉,加在changeIndex后面
else{
oIdiv.style.left = indexIJ_L.ri*49;//临div的原位置给原div
oIdiv.style.top = indexIJ_L.rj*49;
oLdiv.style.left = indexIJ_I.ri*49;//原div的原位置给临div
oLdiv.style.top = indexIJ_I.rj*49;
for(var m=0;m
这样就做到了移动消除,但消除了我们还要再补进来。空缺处上面的要补下来。这里想了想,其实还是数组立功了,我们移动消除完后看7个数组,哪个数组的元素个数小于7,就是哪个竖排有空缺,然后我们想让他们有重力感应,就按照当前元素在数组里的位置重新设定top值就行了,就是index * 49.然后我们再调用创建div的函数把数组补齐,貌似这次数组用的真的是用得非常值!再写个填充div函数:
function fillAllArr(){//补齐所有缺失数组
for(var i=0;i<7;i++){
if(arrAll[i].length<7){//是否缺失
for(var k=0;k
这样在消除后调用这个函数,就补充上了。后来实际运行时我发现我犯了一个
巨大的错误!这里加载时每个数组里的顺序是0-6从上往下加载的,而按我们消除后上面的部到下面来的顺序是完全相反的,按数组里的顺序是里面有元素剔除,后面的会补上来,这样搞完全乱套了!从一开始的数组顺序就用错了,导致后面都跟着错了,崩溃中!
if(ifx-1>=0){//取左侧第一个
var l1 = findColor(ifx-1,ify);
if(ifx-2>=0&&l1==colorI){//如果左侧第一个相同再取第二个,当然不过能边界0
arrLeft[arrLeft.length] = arrAll[ifx-1][ify];
var l2 = findColor(ifx-2,ify);
if(l1==l2)
arrLeft[arrLeft.length] = arrAll[ifx-2][ify];
}
}
应该这样写才行
if(ifx-1>=0){//取左侧第一个
var l1 = findColor(ifx-1,ify);
if(l1==colorI){
arrLeft[arrLeft.length] = arrAll[ifx-1][ify];
if(ifx-2>=0){//如果左侧第一个相同再取第二个,当然不过能边界0
var l2 = findColor(ifx-2,ify);
if(l1==l2)
arrLeft[arrLeft.length] = arrAll[ifx-2][ify];
}
}
}
是分两部判断而不是一棒子打死,所以其它的右,下,上都要改。略
得分:
0
60'
然后我们要有个预备开始,原版的手机游戏是现有开始游戏按钮,进入游戏后有个萌宠喊了一声ready go!就开始了。这里不打算弄多余的按钮,但就一个网页也不能一打开还没准备就开始游戏。所以我这里打算做个3秒的倒数准备,页面显示3,2,1,go!开始。这些倒数显示在一个半透明的div上,页面打开时加载此div,遮住后面的游戏不让点击,显示完3,2,1go后再删除这个div.代码如下:
function beforeBegin() {//显示3,2,1预备倒计时
var oBeginDiv = document.createElement("div");
oBeginDiv.className = "beforeBegin";
$("pa").appendChild(oBeginDiv);
oBeginDiv.innerHTML ="Three!";
setTimeout(function(){
oBeginDiv.innerHTML ="Two!";
setTimeout(function(){
oBeginDiv.innerHTML ="One!";
setTimeout(function(){
oBeginDiv.innerHTML ="Go!";
setTimeout(function(){$("pa").removeChild(oBeginDiv)},500);
},1000);
},1000);
},1000);
}
head 处style添加个样式
.beforeBegin{width: 100%;height: 350px;z-index: 1000;position:absolute;top:60;left: 0;
text-align: center;background-color: gray;
filter:alpha(opacity=50);/*IE*/
opacity:0.5;/*Mozilla*/ };
效果如下:
var iTimeE = 60;
function gameHandler(){//60秒倒计时
if(iTimeE>=0){
var iWidth = $("handleBar").offsetWidth;
$("handleBar").style.width = iTimeE/60*90+"%";
iTimeE-=1;
$("iTime").innerHTML=iTimeE+"'";
setTimeout("gameHandler()",1000);
}
}
body变为
得分:
0
60'
很显然这个倒计时函数是通过对剩余时间与60秒总时间做百分比,然后赋值给原本的进度条长度(90%)做百分比。这里要好好考虑下,没用100%因为60‘时间显示在进度条后面。然后实际运行起来进度条很生硬的。就分60次去变化,每次减少的长度段较多,这样用户体验就不好,我们又没用其他的一些js类库插件啥的,只能增加变化次数,这样每次减少的长度会相应减少。有渐变的效果。
var iTimeE = 60;
function gameHandler(){//60秒倒计时
if(iTimeE>=0){
var iWidth = $("handleBar").offsetWidth;
$("handleBar").style.width = iTimeE/60*90+"%";
iTimeE-=0.05;
$("iTime").innerHTML=Math.ceil(iTimeE)+"'";
setTimeout("gameHandler()",50);
}
}
继续顺着游戏进程写下去,倒计时0时再弹出个div遮罩层显示得分
var bMark = false;//是否可累积分
function deleteDivs(arr){//按数组删除div
for(var n=0;n
然后是
function initCanDelete(){//游戏开始前,结束后,和移动消除后新加载。可组合消除的先消除掉通用
var bInit = false;//是否还有可消除的
for(var i=0;i<7;i++){
for(var j=0;j<7;j++){
var deDivs = ifCanDelete(arrAll[i][j]);
if(deDivs.length>0){
deleteDivs(deDivs);
fillAllArr();//删完后填满
bInit = true;
}
}
}
if(bInit){//为true则证明刚才消除过。
initCanDelete();//刚才消除过则有位置变动还得再遍历一次
}else{
iMark = true;
}
}
将此函数放到init()里。