首页 > 编程语言 >JavaScript写一个连连看的游戏

JavaScript写一个连连看的游戏

时间:2023-01-28 17:34:45浏览次数:74  
标签:function return 游戏 JavaScript obj var 连连看 data target

  天天看到别人玩连连看, 表示没有认真玩过, 不就把两个一样的图片连接在一起么, 我自己写一个都可以呢。

  使用Javascript写了一个, 托管到github, 在线DEMO地址查看:​​打开​

  最终的效果图:

  

JavaScript写一个连连看的游戏_数据

  写连连看之前要先考虑哪些呢?

    1:如何判断两个元素可以连接呢, 刚刚开始的时候我也纳闷, 可以参考这里:​​打开​​;

    2:模板引擎怎么选择呢, 我用了底线库的template,因为语法简单。 本来想用Handlebars,但是这个有点大啊, 而且底线库也提供很多常用工具方法( •̀ ω •́ )y;

    3:布局如何布局呢, 用table, td加上边框, 边框内部一个div,div就是连连看的棋子, 界面更清爽, 简单, 其实直接用canvas写也行, 没认真研究过canvas;

    4:两个元素连接时连线的效果我们要怎么实现呢,如果用dom实现那么需要用到图片,元素连接时候把图片定位到连接的路径。 或者用canvas, 直接用canvas把连接的效果画出来, 我选择后者;

  

  因为我不考虑低浏览器, 使用了zeptoJS库, 基于习惯,把bootstrap也引用了;

  使用了三个主要构造函数, 包括Data, View, Score

 

  View的结构如下, 东西比较少 包括事件绑定界面生成, 以及当两个相同元素消失时的 绘图效果

View

/**
* @desc 根据数据生成map
* */
renderHTML : function

/**
* @desc 界面的主要事件绑定
* @return this;
* */
bindEvents : function


/**
* @desc 工具方法,在canvas上面进行绘图;
* @param [{x:0,y:0},{x:1,y:1},{x:2,y:2},{x:3,y:3}]一个数组, 会自动重绘;
* */
showSparkLine : function

  tbody内部元素的模板是这样的:

<script type="text/template" id="tr-td-tpl">
<% for(var i=0; i<data.length; i++) {%>
<tr>
<% for(var j=0; j< data[i].length; j++ ) { %>
<td id="<%=i%><%=j%>" class="bg<%=data[i][j]%>" data-x="<%=j%>" data-y="<%=i%>" data-data="<%=data[i][j]%>" data-info='{"x":<%=[j]%>,"y":<%=[i]%>}'>
<div>
<%=getImg(data[i][j])%>
</div>
</td>
<% } %>
</tr>
<% } %>
</script>

  上面代码的getImg方法会调用全局window的getImg方法,这个方法是根据数据生成图片字符串, 是一个辅助的函数

window.getImg = function( num ) {
switch(num){
case 1:
return "<img src='imgs/ani (1).gif' />";
case 2:
return "<img src='imgs/ani (2).gif' />";
case 3:
return "<img src='imgs/ani (3).gif' />";
case 4:
return "<img src='imgs/ani (4).gif' />";
case 5:
return "<img src='imgs/ani (5).gif' />";
case 6:
return "<img src='imgs/ani (6).gif' />";
}
};

 

  因为连连看的数据是个二维的数组, 所以模板中必须使用两个for循环, 循环产生HTML字符串, 如果把数据和模板合在一起, 会生成下图的DOM结构:

JavaScript写一个连连看的游戏_i++_02

 

  分数模块构造函数Score,  所有有关得分的代码就这些了  (把元素传进去, 直接调用生成实例的addScore方法, 会自动渲染DOM), 为分数单独写一个构造函数是因为为了解耦

Score = function(el) {
this.el = $(el);
this.score = 0;
};

$.extend( Score.prototype , {
/**
* @desc 改变元素的HTML,递增分数;
* @param
* */
addScore : function() {
this.el.html(++this.score);
}
});

 

  构造函数Data, 主要的结构如下 , 虽然方法比较少, 实际上Data这块代码占了300行.... 要判断元素是否可以连接用canConnect方法,canConnect方法又会调用dirConnect方法, 计算比较繁琐, 想了解的话最好自己写写:

//新建初始化
newData : function

//工具方法,随机混肴数组;
suffer : function

/**
* @desc set值,把地图中对应的数据清空或者设置,两用接口
* @param x, y
* @return chain
* */
set : function

/**
* @desc 判断两个元素之间是否可以连接
* @param [{x:1,y:1},{x:1,y:1}]
* @return false || []
* */
canConnect : function

/**
* @desc 判断元素是否可以直连
* @param [{x:1,y:1},{x:1,y:1}]
* @return false || true
* */
dirConnect

 

  所有所有代码如下, 作为参考:

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<!-- 新 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css">
<title>link</title>
<script src="js/zepto.js"></script>
<script src="js/underscore1.8.js"></script>
<style>
table{
border-collapse: collapse;
}
td{
border:1px solid #f5f5f5;
text-align: center;
line-height: 40px;
cursor: pointer;
}
td.active{
opacity: 0.7;
}
td div{
width:40px;
height:40px;
}
.bg1{
/*background: #2ECC71;*/
}
.bg2{
/*background: #E67E22;*/
}
.bg3{
/*background: #34495E;*/
}
.bg4{
/*background: #1ABC9C;*/
}
.relative{
position: relative;
}
.absolute{
position: absolute;
left:0;
top:0;
}
</style>
</head>
<body>

<div class="container ">
<div class="row" style="width:80%;margin:0 auto;">
<h3>得分<span class="label label-default" id="score">0</span></h3>
</div>
</div>

<div class="container">
<div class="row relative">
<table class="absolute">
<thead></thead>
<tbody id="tbody">
</tbody>
</table>
<canvas id="canvas">
<p>Your browserdoes not support the canvas element.</p>
</canvas>
</div>
</div>
<script type="text/template" id="tr-td-tpl">
<% for(var i=0; i<data.length; i++) {%>
<tr>
<% for(var j=0; j< data[i].length; j++ ) { %>
<td id="<%=i%><%=j%>" class="bg<%=data[i][j]%>" data-x="<%=j%>" data-y="<%=i%>" data-data="<%=data[i][j]%>" data-info='{"x":<%=[j]%>,"y":<%=[i]%>}'>
<div>
<%=getImg(data[i][j])%>
</div>
</td>
<% } %>
</tr>
<% } %>
</script>
<script>
var el = document.getElementById("tbody");
var elCan = document.getElementById("canvas");
var tpl = document.getElementById("tr-td-tpl");

var cfg = {
width : 8,
height : 8
};
window.getImg = function( num ) {
switch(num){
case 1:
return "<img src='imgs/ani (1).gif' />";
case 2:
return "<img src='imgs/ani (2).gif' />";
case 3:
return "<img src='imgs/ani (3).gif' />";
case 4:
return "<img src='imgs/ani (4).gif' />";
case 5:
return "<img src='imgs/ani (5).gif' />";
case 6:
return "<img src='imgs/ani (6).gif' />";
}
};

var View = function(data, score) {
this.data = data;
this.score = score;
},
Data = function(cfg) {
this.cfg = {
width : cfg.width+2,
height : cfg.height+2
};
this.getRandom = this.getRandom();
},
Score = function(el) {
this.el = $(el);
this.score = 0;
};

$.extend( Data.prototype, {
/**
* @desc 把两个
* @param HTMLELEMENT
* @return true || false
* */
clear : function(obj, target) {
},
/**
* @desc 根据this.cfg新建数据到this.map
* @param void
* @return void
* */
newData : function() {
var result = [];
for(var i=0; i<=this.cfg.height+1; i++ ) {
result[i] = result[i] || [];
for(var j = 0; j<= this.cfg.width+1; j++) {

if(i === 0 || j===0 || (i===this.cfg.height+1) || j === (this.cfg.width+1) ) {
result[i][j] = 0;
}else{
//1-4
result[i][j] = this.getRandom();
}
};
};
this.map = result;
return this;
},
//随机混肴数组;
suffer : function(obj) {
function random(min, max) {
if (max == null) {
max = min;
min = 0;
}
return min + Math.floor(Math.random() * (max - min + 1));
};
var set = obj;
var length = set.length;
var shuffled = Array(length);
for (var index = 0, rand; index < length; index++) {
rand = random(0, index);
if (rand !== index) shuffled[index] = shuffled[rand];
shuffled[rand] = set[index];
}
return shuffled;
},
/**
* @return 返回值必须是成双的, 消除到最后尼玛,发现有一堆不匹配的,玩个球;
* */
getRandom : function() {
//如果消消乐是3*3, 那么你告诉我....最后一个和谁消, 所以要做的就是把所有的元素生成变成一半,然后返回;
var arr = new Array( (this.cfg.height) * (this.cfg.width) / 2 );
var result = [];
for(var i=0; i<arr.length; i++ ) {
arr[i] = (Math.floor( Math.random()*6 ) + 1);
};
result = Array.prototype.concat.call( [] , arr, arr);
result = this.suffer( result );
return function( ) {
return result.pop();
};
},
/**
* @desc set值
* @param x, y
* @return chain
* */
set : function( x, y) {
this.map[y][x] = 0;
return this;
},
/**
* @desc 判断元素是否可以连接
* @param [{x:1,y:1},{x:1,y:1}]
* @return false || true
* */
canConnect : function(obj,target) {
var map = this.map;
//循环obj的y轴相等 , obj.x旁边所有数据为0的元素;;
var getX = function( obj ) {
var result = [];
//循环找出在X附近为0的元素;
for(var i=obj.x+1; i< map[0].length; i++) {
if( map[obj.y][i] == 0 ) {
result.push( {x:i, y:obj.y} );
}else{
break;
};
};
for(var i=obj.x-1; i>=0; i--) {
if( map[obj.y][i] == 0 ) {
result.push( {x:i,y:obj.y} );
}else{
break;
};
};
return result;
};
//循环obj的x轴相等, obj.y旁边所有数据为0的元素;
var getY = function(obj) {
var result = [];
for(var i=obj.y+1; i<map.length; i++) {
if( map[i][obj.x] == 0) {
result.push( { x : obj.x ,y : i} );
}else{
break;
};
};
for(var i=obj.y-1; i>=0; i--) {
if( map[i][obj.x] == 0 ) {
result.push( { x : obj.x ,y : i} );
}else{
break;
};
};
return result;
};
var arr0 = Array.prototype.concat.call( [], getX(obj), obj, getY(obj)).filter(function(obj) {
return !!obj;
});
var arr1 = Array.prototype.concat.call( [], getX(target), target, getY(target) ).filter(function(obj) {
return !!obj;
});
for(i = 0; i<arr0.length; i++) {
for(var j = 0; j<arr1.length; j++) {
//只要有一个连接就返回true;
if( this.dirConnect(arr0[i],arr1[j]) ) {
return [obj, arr0[i], arr1[j], target];
};
};
};
return false;
},
/**
* @desc 判断元素是否可以直接连接
* @param [{x:1,y:1},{x:1,y:1}]
* @return false || true
* */
dirConnect : function(obj, target) {
var map = this.map;
//row是x轴 列
//col是y轴 行
var min = 0, max = 0, sum = 0;
if(obj.y === target.y) {
if(obj.x < target.x) {
min = obj.x;
max = target.x;
}else{
min = target.x;
max = obj.x;
};
for(var i=min; i<=max; i++) {
sum += map[obj.y][i];
};
if(sum === (map[obj.y][obj.x] + map[target.y][target.x])) {
return true;
}else{
return false;
};
};
if(obj.x === target.x) {
if(obj.y < target.y) {
min = obj.y;
max = target.y;
}else{
min = target.x;
max = obj.y;
};
for( i=min; i<=max; i++) {
sum += map[i][obj.x];
};
if( sum === (map[obj.y][obj.x] + map[target.y][target.x])) {
return true;
}else{
return false;
};
};
}
});
$.extend( View.prototype, {
/**
* @desc 为view添加视图的主元素
* @return void
* */
setEL : function(el) {
this.el = el;
return this;
},
setTpl : function(tpl) {
this.tpl = _.template( tpl.innerHTML );
return this;
},
/**
* @desc 根据数据生成map
* */
renderHTML : function() {
$(this.el).html( this.tpl( {data : this.data.map} ) );
return this;
},
/**
* @desc 界面的主要事件绑定
* @return this;
* */
bindEvents : function() {
$(this.el).delegate("td", "click", this.click.bind(this));
return this;
},
/**
* @desc click事件, 单独抽出来的;
* */
click : function(ev) {
//修改样式;
$("td.active").removeClass("active");
var target = $(ev.target).closest("td");
target.addClass("active");

//第一次点击我们做的特殊处理;
var prev = this.prev;
if( !prev || target[0] === prev[0]){
this.prev = target;
return;
};

if( prev.attr("data-data") === target.attr("data-data")) {
var xy = JSON.parse( prev.attr("data-info") );
var xxyy = JSON.parse( target.attr("data-info") );
//保存了连接的数组信息
var connectionInfo = [] || false;
if( connectionInfo = this.data.canConnect( xy, xxyy) ) {
this.showSparkLine( connectionInfo );
this.prev = undefined;
this.data.set(xy.x, xy.y);
this.data.set(xxyy.x, xxyy.y);
this.score.addScore();
var _this = this;
setTimeout(function() {
_this.renderHTML();
},2000);
};
prev.attr("data-data", "");
target.attr("data-data","")
}else{
this.prev = target;
};
},
/**
* @desc 工具方法,在canvas上面进行绘图;
* @param [{x:0,y:0},{x:1,y:1},{x:2,y:2},{x:3,y:3}]一个数组, 会自动重绘;
* */
showSparkLine : function( arr ) {
arr = arr.map(function(xy) {
return {
x : (xy.x)*40 + 20,
y : (xy.y)*40 + 20
}
});
var elCan = document.getElementById("canvas");
function spark(ctx) {
function showAndClear(arr, lineWidth) {
ctx.clearRect(0,0,elCan.width,elCan.height);
ctx.beginPath();
ctx.lineJoin = "round";
ctx.lineWidth = lineWidth;
ctx.shadowColor = "rgba(241, 196, 15, 0.41)";
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 1;
ctx.shadowBlur = 1;
for(var i=0; i<arr.length-1; i++) {
var xy = arr[i];
var nextXY = arr[i+1]
ctx.moveTo(xy.x, xy.y);
ctx.lineTo(nextXY.x, nextXY.y);
};
ctx.stroke();
};
var ctx = elCan.getContext("2d");
ctx.strokeStyle = "#F1C40F";
var lineWidthArr = [1,2,1,2,1,3,1,0];
var len = lineWidthArr.length;
var times = 400, addTimes = 200;
while(len--) {
(function(len){
setTimeout(function() {
showAndClear(arr, lineWidthArr[len]);
if(len==0) {
ctx.clearRect(0,0,elCan.width,elCan.height);
}
}, times);
times += addTimes;
})(len)
};
};
spark( elCan );
}
});
$.extend( Score.prototype , {
/**
* @desc 改变元素的HTML,递增分数;
* @param
* */
addScore : function() {
this.el.html(++this.score);
}
});

$(function() {
var score = new Score( document.getElementById("score") );
var data = new Data(cfg).newData();
var view = new View(data, score);
view.setEL( el ).setTpl( tpl).renderHTML().bindEvents();
(function init() {
//如果通过style属性添加width或者height,会根据原来的宽和高度自动伸缩的
elCan.width = el.offsetWidth;
elCan.height = el.offsetHeight;
})();
});

</script>
</body>
</html>

View Code

  在线DEMO地址查看:​​打开​

  找到了一个别人写的连连看, 代码极少, 作为参考吧:

JavaScript写一个连连看的游戏_i++_03

JavaScript写一个连连看的游戏_数据_04

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title> 连连看 </title>
<meta name="Generator" content="EditPlus">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<style type="text/css">
#board{width:508px; height:500px; margin: 30px auto 0px; overflow: hidden; position: relative; background-color: #999999;}
#board span{display: block; position: absolute; width: 30px; height: 30px; }
</style>
</head>
<body>
<div id="board" >
</div>
</body>

<!-- js -->
<script src="http://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript" >

$(function(){
var cont=$("#board");
var colors=["#ff0000","#00ff00","#0000ff","#ffcc33","#000000","#00ffcc","#ffffff"];
var pos=[];
var click=0;
var firstSpan;
var fx;
var fy;
var arr=[];

arr=[0,0,0,0,0,0,0,0];
pos.push(arr);

for(var i=0;i<8;i++){
new creSpan(i,cont,0,i*40,colors[6],0);
}

for(var i=1;i<=6;i++){
m=new creSpan(i,cont,i*40,0,"#ffffff");
arr=[0];

for(var j=0;j<6;j++){
var color=Math.floor(Math.random()*6);
new creSpan(i,cont,i*40,(j+1)*40,colors[color],(color+1));
arr.push(1);
}
m=new creSpan(i,cont,i*40,(j+1)*40,"#ffffff",0);
arr.push(0);
pos.push(arr);

}
for(var i=0;i<8;i++){
m=new creSpan(i,cont,7*40,i*40,"#ffffff",0);
}
arr=[0,0,0,0,0,0,0,0];
pos.push(arr);

function clear(c1,c2,x,y){
if(c1!=null)c1.style.background="#ffffff";
if(c2!=null){
c2.style.background="#ffffff";
pos[x-1][y-1]=0;
pos[fx-1][fy-1]=0;
}
fx=0;
fy=0;
click=0;
}

$.each($("#board span"),function(index,mSpan){
$(this).click(function(){
var x=Math.floor(index/8);
var y=Math.floor(index%8);
if(click==0){
click=1;
firstSpan=mSpan;
fx=x;
fy=y;
return;
}

if(firstSpan.id!=mSpan.id||(x==fx&&fy==y)){
clear(null,null,0,0);
return;
}
var col=6;
var row=6;

for(var i=0;i<row+2;i++){
var step=i-x>0?1:-1;
var count=0;
for(var j=x;j!=i;j+=step){
count+=pos[j][y];
}
step=y>fy?-1:1;
for(j=y;j!=fy;j+=step){
count+=pos[i][j];
}
step=i>fx?-1:1;
for(j=i;j!=fx;j+=step){
count+=pos[j][fy];
}
if(count==1){
clear(firstSpan,mSpan,x,y);
return;
}
}
for(i=0;i<col+2;i++){
step=i-y>0?1:-1;
count=0;
for(j=y;j!=i;j+=step){
count+=pos[x][j];
}
step=x>fx?-1:1;
for(j=x;j!=fx;j+=step){
count+=pos[i][j];
}
step=i<fy?1:-1;
for(j=i;j!=fy;j+=step){
count+=pos[fx][j];
}
if(count==1){
clear(firstSpan,mSpan,x,y);
return;
}
}
clear(null,null,0,0);

});
});
});

function creSpan(n,cont,mtop,mleft,mcolor,idstr){
var mSpan=document.createElement("span");
cont[0].appendChild(mSpan);
mSpan.id=idstr;
with(mSpan.style){
top=mtop+"px";
left=mleft+"px";
background=mcolor;
}
};

</script>
</html>

View Code

 

  这个可是好东西啊, 有想要的么

JavaScript写一个连连看的游戏_数据_05

 

   话说poppin越跳越怪了,shit, 怎莫办啊,​​打开​

作者: ​​NONO

天道酬勤



标签:function,return,游戏,JavaScript,obj,var,连连看,data,target
From: https://blog.51cto.com/u_12304260/6025054

相关文章

  • JavaScript 获取用户选择的文本
    constgetSelectedText=()=>window.getSelection().toString();getSelectedText();JQuery实例:$(function(){constgetSelectedText=()=>window.getSelect......
  • 数据可视化大屏高德地图javascript webAPI开发的智慧治安物联网管理系统实战解析(web
    文章目录​​高德地图开发系列文章目录​​​​前言​​​​一、项目说明​​​​二、核心代码开发​​​​1.引入库​​​​2.构建DOM容器​​​​3.高德地图开发​​​​(1......
  • JavaScript学习笔记—正则表达式
    用来定义一个规则通过这个规则计算机可以检查一个字符串是否符合规则或者将字符串中符合规则的内容提取出来也是JS中的一个对象,所以要使用正则表达式,需要先创建正则表达......
  • JavaScript基础知识
    1.编程语言  1.1编程      1.2计算机语言    1.3编程语言    1.4翻译器      1.5编程语言和标记语言的区别   ......
  • 用 JavaScript 制作一个新年的日历小工具
    2023年,让我们携手一道兔谋大业、做出兔出贡献!同打拼、共丰盈、多喜乐、长安宁,好运一路相随兔气扬眉!......
  • JavaScript – Group / GroupToMap
    前言arraygroupby是一个很常见的功能.但JS却没有build-in方法.一直到es2023才有group和groupToMap(目前没有任何游览器支持,但已经有polyfill了).这篇......
  • JavaScript学习笔记—包装类
    1.描述字符串本质就是一个字符数组"hello"-->["h","e","l","l","o"]2.属性和方法(1)length获取字符串的长度(2)字符串[index]获取指定位置的字符(3)at(index)......
  • 基于EasyX和Raylib的打字母游戏
    原版代码地址https://codebus.cn/yangw/letters-shooting-game基于Raylib实现时,由于Raylib需要显式设置FPS,getchar这样的调用是不能用的。因此一开始的welcom......
  • JavaScript学习笔记—Date
    在JS中所有的和时间相关的数据都由Date对象来表示对象的方法(1)getFullYear()返回当前日期的年份(4位)(2)getMonth()返回当前日期的月份(0-11)(3)getDate()返回当前日期的几......
  • JavaScript学习笔记—Math
    工具类为我们提供了数学运算相关的一些常量和方法常量(1)Math.PI圆周率方法(1)Math.abs()求一个数的绝对值(2)Math.min()求多个值中的最小值(3)Math.max()求多个值中的......