jQuery进阶——分析拼图游戏源代码
在网上看到一款拼图游戏游戏,发现它是js写成的,于是想看一下它的实现方法,经过代码去余冗和修改,我们来分析这段代码的精妙。
1. HTML部分
复制网页的源代码,去掉与拼图功能无关,并根据CSS文件去掉具体内容标签,得到一个简单的HTML页面
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>jQuery拼图</title> <script type="text/javascript" src="jquery.min.js"></script> <script type="text/javascript" src="func.js"></script> <link href="style.css" type="text/css" rel="stylesheet"> </head> <body> <div id="play_area"></div> <div class="play_menu"> <a id="play_btn_start" class="play_btn" href="javascript:void(0);" unselectable="on">洗牌</a> <a id="play_btn_level" class="play_btn" href="javascript:void(0);" unselectable="on">HARD</a> <div class="level_menu" id="play_menu_level"> <ul> <li> <a href="javascript:void(0);" level="0">EASY</a> </li> <li> <a href="javascript:void(0);" level="1">MIDDLE</a> </li> <li> <a href="javascript:void(0);" level="2">HARD</a> </li> </ul> </div> </div> </body> </html>
CSS定义
html { height: 100%; } body { font-family: "Helvetica Neue", "Hiragino Sans GB", "Segoe UI", "Microsoft Yahei", Tahoma, Arial, STHeiti, sans-serif; font-size: 12px; background: #fff; color: #333; } a { outline: none; -moz-outline: none; text-decoration: none; } #play_area { position: relative; width: 300px; height: 300px; background: #fefefe; box-shadow: 0px 0px 8px #09F; cursor: default; } #play_area .play_cell { width: 48px; height: 48px; border: 1px solid #fff; border-radius: 4px; position: absolute; background-position: 5px 5px; cursor: default; z-index: 80; box-shadow: 0px 0px 8px #fff; transition-property: background-position; transition-duration: 300ms; transition-timing-function: ease-in-out; } #play_area .play_cell.hover { filter: alpha(opacity=80); opacity: .8; box-shadow: 0px 0px 8px #000; z-index: 90; } .play_menu { margin-left: 130px; font-size: 14px; padding-top: 20px; } .play_menu a.play_btn { display: block; margin-bottom: 20px; width: 80px; height: 28px; line-height: 28px; text-align: center; text-decoration: none; color: #333; background: #fefefe; border: 1px solid #eee; border-radius: 2px; box-shadow: 1px 1px 2px #eee; border-color: #ddd #d2d2d2 #d2d2d2 #ddd; outline: none; -moz-outline: none; } .play_menu a.play_btn:hover { background-color: #fcfcfc; border-color: #ccc; box-shadow: inset 0 -2px 6px #eee; } .play_menu a#play_btn_level { position: relative; margin-bottom: 30px; } .level_text { margin-left: -10px; } .level_menu { position: absolute; margin: -30px 0 0px 1px; display: none; } .level_menu ul { list-style: none; } .level_menu li { float: left; } .level_menu li a { display: block; padding: 3px 10px; border: 1px solid #e8e8e8; margin-left: -1px; color: #09c; } .level_menu li a:hover { background: #09c; color: #fefefe; }
2. 脚本func.js事件代码分析
先有一个构造函数,定义游戏中需要的窗口大小信息,和图片CSS信息保存的数据结构,最后调用init初始化拼图格,调用menu定义‘洗牌’和难度选择的事件。
var puzzleGame = function(options) { this.img = options; this.areaWidth = parseInt($("#play_area").css("width")); this.areaHeight = parseInt($("#play_area").css("height")); this.offX = $("#play_area").offset().left; this.offY = $("#play_area").offset().top; this.levelArr = [3, 4, 6]; this.level = 2; this.cellRow = this.levelArr[this.level]; this.cellCol = this.levelArr[this.level]; this.cellWidth = this.areaWidth / this.cellCol; this.cellHeight = this.areaHeight / this.cellRow; this.imgArr = []; // 原图序列 this.ranArr = []; // 随机序列 this.cellArr = []; // CSS属性的引用 this.thisLeft = 0; this.thisTop = 0; this.nextIndex this.thisIndex this.cb_cellDown = $.Callbacks(); this.init(); this.menu(); };
接下来为类定义方法,init是按照对格子的划分方法,创建div赋予backgroundPosition的属性,并把每个div的引用存在数组cellArr中。
imgArr为一个0到15的数组,作为正确序列。它在后面被复制随机化后来与正确序列作比较判断是否拼图成功。
puzzleGame.prototype = { init : function() { var _cell; for (var i = 0; i < this.cellRow; i++) { for (var j = 0; j < this.cellCol; j++) { _cell = document.createElement("div"); _cell.className = "play_cell"; $(_cell).css({ "width" : this.cellWidth - 2, "height" : this.cellHeight - 2, "left" : j * this.cellWidth, "top" : i * this.cellHeight, "background" : "url(" + this.img + ")", "backgroundPosition" : (-j) * this.cellWidth + "px " + (-i) * this.cellHeight + "px" }); this.imgArr.push(i * this.cellCol + j); this.cellArr.push($(_cell)); $("#play_area").append(_cell); } } }, menu : function() { var self = this; $("#play_btn_start").click(function() { self.play(); }); $("#play_btn_level").click(function() { $("#play_menu_level").toggle(); }); $("#play_menu_level").find("a").click(function() { $("#play_menu_level").hide(); $("#play_btn_level").html($(this).html()); if (parseInt($(this).attr("level")) !== self.level) { self.level = $(this).attr("level"); self.cellRow = self.levelArr[self.level]; self.cellCol = self.levelArr[self.level]; self.cellWidth = self.areaWidth / self.cellCol; self.cellHeight = self.areaHeight / self.cellRow; self.init(); } }) } }
下一段是开始洗牌的事件,它的随机化策略是,用复制一个原数列每次随机选择一个,记录下每次选择并根据cellArr的引用修改对应CSS
play : function() { this.randomImg(); // 定义鼠标事件 this.bindCell(); }, // 创建随机序列ranArr并改变cellArr的CSS属性 randomImg : function() { // 复制数组 var arr = this.imgArr.slice(); for (var i = 0, ilen = arr.length; i < ilen; i++) { // 随机删除一个 var tmp = Math.floor(Math.random() * arr.length); this.ranArr.push(arr[tmp]); this.cellArr[i].css({ "backgroundPosition" : (-arr[tmp] % this.cellCol) * this.cellWidth + "px " + (-Math.floor(arr[tmp] / this.cellCol)) * this.cellHeight + "px" }) // 删除 arr.splice(tmp, 1); } },
3.脚本func.js回调
从play被调用生成随机后的拼图,调用bindCell。
它为回调函数列表添加了cellDown方法,在遇到mousedown事件时,传参窗口/块/类调用cellDown
更多$.callbacks回调函数列表用法:http://www.cnblogs.com/snandy/archive/2012/11/15/2770237.html .
bindCell : function() { var self = this; this.cb_cellDown.add(self.cellDown); for (var i = 0, len = this.cellArr.length; i < len; i++) { this.cellArr[i].on({ "mouseover" : function() { $(this).addClass("hover"); }, "mouseout" : function() { $(this).removeClass("hover"); }, "mousedown" : function(e) { self.cb_cellDown.fire(e, $(this), self); return false; } }); } }
在mouseup时撤销mouseup和mousemove事件监听,清空回调函数列表,每个块不再执行mousedown时cellDown事件
为了等待在移动的块移到对应位置后再启用cellDown监听。
// 放下 cellDown : function(e, _cell, self) { var _x = e.pageX - _cell.offset().left, _y = e.pageY - _cell.offset().top; self.thisLeft = _cell.css("left"); self.thisTop = _cell.css("top"); self.thisIndex = Math.floor(parseInt(self.thisTop) / self.cellHeight) * self.cellCol; self.thisIndex += Math.floor(parseInt(self.thisLeft) / self.cellWidth); _cell.css("zIndex", 99); $(document).on({ "mousemove" : function(e) { _cell.css({ "left" : e.pageX - self.offX - _x, "top" : e.pageY - self.offY - _y }) }, "mouseup" : function(e) { $(document).off("mouseup"); $(document).off("mousemove"); self.cb_cellDown.empty(); if (e.pageX - self.offX < 0 || e.pageX - self.offX > self.areaWidth || e.pageY - self.offY < 0 || e.pageY - self.offY > self.areaHeight) { self.returnCell(); return; } var _tx, _ty, _ti, _tj; _tx = e.pageX - self.offX; _ty = e.pageY - self.offY; _ti = Math.floor(_ty / self.cellHeight); _tj = Math.floor(_tx / self.cellWidth); self.nextIndex = _ti * self.cellCol + _tj; if (self.nextIndex == self.thisIndex) { self.returnCell(); } else { self.changeCell(); } } }) },
定义交换和返回的动画,修改ranArr的引用,在动画结束后再向回调函数列表添加mousedown的cellDown事件,应对下一次拼图块的移动。
// 交换 changeCell : function() { var self = this, _tc = this.cellArr[this.thisIndex], _tl = this.thisLeft, _tt = this.thisTop, _nc = this.cellArr[this.nextIndex], _nl = (this.nextIndex % this.cellCol) * this.cellWidth, _nt = Math.floor(this.nextIndex / this.cellCol) * this.cellHeight; _nc.css("zIndex", 98); this.cellArr[this.nextIndex] = _tc; this.cellArr[this.thisIndex] = _nc; this.ranArr[this.nextIndex] = this.ranArr[this.nextIndex] + this.ranArr[this.thisIndex]; this.ranArr[this.thisIndex] = this.ranArr[this.nextIndex] - this.ranArr[this.thisIndex]; this.ranArr[this.nextIndex] = this.ranArr[this.nextIndex] - this.ranArr[this.thisIndex]; _tc.animate({ "left" : _nl, "top" : _nt }, 500, function() { _tc.removeClass("hover"); _tc.css("zIndex", ""); }) _nc.animate({ "left" : _tl, "top" : _tt }, 500, function() { _nc.removeClass("hover"); _nc.css("zIndex", ""); if (self.ranArr.join() == self.imgArr.join()) { alert("ok"); } if (!self.cb_cellDown.has(self.cellDown)) self.cb_cellDown.add(self.cellDown); }) }, // 返回 returnCell : function() { var self = this; this.cellArr[this.thisIndex].animate({ "left" : self.thisLeft, "top" : self.thisTop }, 500, function() { $(this).removeClass("hover"); $(this).css("zIndex", ""); if (!self.cb_cellDown.has(self.cellDown)) self.cb_cellDown.add(self.cellDown); }); }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。