思路:
-
上传一张自己的本地的图片
-
将图片分割成若干行若干列
+ 给每一个小格子,添加上自己的索引
+ 把索引存入一个数组中,方便最后判断是否完成
-
分割的每一个小格子上都显示完整图片对应的一部分
-
点击开始游戏按钮,打乱图片的位置
+ 创建一个新数组,将原数组打乱之后存到新数组中
+ 让每一个小格子根据打乱后的数组进行定位
-
点击两个不同的小格,让其交换位置。
+ 小格子位置交换完成后,需要将打乱的数组对应的两个值也交换位置。
-
判断交换后的乱序数组和原来的数组是否相等,如果相等即表示游戏完成。
难点:
-
如何上传本地图片让其显示的
-
如何将一张图片切割成多个小格子
-
如果根据乱序的数组来计算当前的行数和列数
解决办法:
-
针对于如何上传一张本体图片,看之前的一篇关于浏览器如何在页面上显示准备上传的图片的问题,下面是传送门。
+ <a target = "_blank" href="http://www.jianshu.com/p/9e4a1ee03089">关于浏览器如何在页面上显示准备上传的图片的问题</a>
-
针对如何切割一张图片的问题,事实上也并不能说是切割,且听我细细道来。
+ 首先我们利用两个for循环,外层代表行数,里面代表列数,然后动态创建div来表示每一个小方格。
+ 然后我们分隔好了小方格,我们也知道小方格的宽和高,并且知道每行每列各有多少个小方格。
+ 之后我们给每一个小方格都设置一个`background-image`属性,让每一个方格都有一个完整的图。所以说这里并不是一张背景图,而是有多少个小方格就有多少张图片。
+ 再之后根据行数和列数的数量来设置`background-size`,比如只有一个格子,`background-size`的值就是 100% ,同理,两个值就是200%
+ 最后根据当前第几行,第几列,每一个小格子的宽度和高度来计算出偏移量,即left值和top值。再利用`background-position`来进行定位即可实现。
-
针对根据乱序数组来计算当前的行数和列数。这里其实也并不难,只是我个人卡在这里卡了一会儿。。。。
+ 首先我们的乱序数组是根据原来的数组打乱顺序而来,所以里面的数值除了顺序应该就是一样的。
+ 当我们点击图片的时候能够获取到图片所在的div的索引。
+ 因为原数组是按照0,1,2,3,4.....这样排列的,所以这个索引在乱序数组中对应的值就是打乱后的图片是从0开始数的第多少张图片。
+ 知道了这张图片此时是属于第几张,我们就能根据这个值来计算出这张图片此时对应的行数和列数。
- 当前是第几张图片 / 一行多少列 然后取整 就是当前属于第几行
- 当前是第几张图片 % 一行多少列 然后取余 就是当前属于第几列
这里我用一张动图来表示如果切割图片的
注意点:
-
打乱索引 的时候要注意,由于打乱的数组可能跟原数组还是一样,所以需要控制一下。
-
上传的本地的图片不要太大,不然读取的速度那还真有点慢。。。
-
上传的图片需要是个正方形的,实在没有正方向的话,高度大于宽度的图片也行,无非是下半部分被干掉了。。
-
因为个人比较懒。很多地方没有写兼容,所以打开小游戏请使用chrome浏览器
我们来看看关键代码
-
HTML部分
<!-- 获取图片 -->
<input type="file" id="file">
<!-- 字体图标 -->
<div id="btn">
<i class="iconfont icon-xiangji2"></i>
</div>
<div id="gameArea">
<!-- 游戏开始按钮 -->
<div id="gameStart">点击开始</div>
<!-- 图片存放的区域 -->
<div id="imgArea"></div>
</div>
-
CSS部分
css部分真没啥好说的,有兴趣的话大家下载源码,一看就知道了
-
JS部分
- 切割图片
// 切割图片 imgSplit:function(){ // 清空图片存放区域 this.imgArea.innerHTML = ''; // 用来存方动态创建的div元素 var _cell = ''; // 行数 for (var i = 0, l =this.leverArr[0]; i<l ;i++) { // 列数 for (var j = 0,l =this.leverArr[1]; j<l ;j++) { // 给每张图片一个索引值 // 索引递增规则为 从左到右 从上到下 this.imgOrigArr.push(i*this.leverArr[0] + j); // 创建div _cell = document.createElement('div'); // 给div添加id _cell.className = "imgCell"; // 给每张一个索引,方便后面点击的时候进行判断 _cell.index = i*this.leverArr[0] + j; // 给div添加样式 _cell.style.width = this.cellWidth+'px'; _cell.style.height = this.cellHeight+'px'; _cell.style.left = j*this.cellWidth+'px'; _cell.style.top = i*this.cellHeight + 'px'; _cell.style.backgroundImage= "url("+this.imgUrl+")"; // 这里因为100%就让背景图的大小等于了一个小格子的大小 // 而我们只需要原始图的一部分,并不是想缩小原图 // 所以根据小格子的个数来放大图片 _cell.style.backgroundSize = this.leverArr[1]+'00%'; // 移动背景图,行成最后切成的一块块的效果 _cell.style.backgroundPosition = (-j)*this.cellWidth + 'px ' + (-i)*this.cellHeight+'px'; // 让背景图从边框开始平铺 _cell.style.backgroundOrigin = "border-box"; this.imgArea.appendChild(_cell); } } // 获取小格子的dom元素 this.imgCells = document.querySelectorAll('.imgCell'); //将选择图片的按钮移动到可视区域外 this.btnObj.style.left= -this.btnObj.offsetWidth+'px'; // 将图片移入可视区域 // this.gameAreaObj.style.background = 'url('+this.imgUrl +')'; this.gameAreaObj.style.left = '50%'; this.gameAreaObj.style.transform= 'translate(-50%,-50%)'; // 使按钮绑定事件,点击按钮开始整个游戏 this.gameStartBtnObj.onclick = this.clickHandle(); }
- 打乱图片索引
// 打乱图片索引 randomArr:function(){ // 清空乱序数组 this.imgRandomArr = []; // 判断原来的数组是否和乱序数组一样 var _flag = true; // 遍历原始索引 for(var i=0,l=this.imgOrigArr.length;i<l;i++){ // 获取从0到数组长度之间的一个索引值 var order = Math.floor(Math.random()*this.imgOrigArr.length); // 如果乱序数组中没有值就直接添加 // 否则就在这个乱序数组中找对应的随机数的索引,找不到就添加,找到就继续随机 if(this.imgRandomArr.length>0){ while(this.imgRandomArr.indexOf(order) >-1){ order = Math.floor(Math.random()*this.imgOrigArr.length); } } this.imgRandomArr.push(order); } // 判断乱序数组和原始数组是否一样 if(this.imgRandomArr.length === this.imgOrigArr.length){ // 遍历数组 for(var i=0,l=this.imgOrigArr.length;i<l;i++){ if(this.imgRandomArr[i] != this.imgOrigArr[i]){ _flag = false; break; }else{ _flag = true; } } }else{ _flag = true; } // 返回值为true的话 就代表原始数组和乱序数组一致,重新打乱数组 if(_flag){ this.randomArr(); } }
- 交换两次点击的图片位置
// 交换两次点击的图片位置 cellExchange:function(from,to){ // 因为图片此时的排序是根据 以图片的索引值为索引 // 在乱序的数组中根据相对应的索引取出的值作为当前图片的排序位置的 // 因此根据from to这两个值作为索引,就能在乱序数组中得到当前图片是第几张 // 求出from的图片 是第几行第几列 // 当前是第几张图片 / 一行多少列 然后取整 就是当前属于第几行 var _fromRow = Math.floor(this.imgRandomArr[from] / this.leverArr[1]); // 当前是第几张图片 % 一行多少列 然后取余 就是当前属于第几列 var _fromCol = this.imgRandomArr[from] % this.leverArr[1]; // 求出to的图片 是第几张第几列 var _toRow = Math.floor(this.imgRandomArr[to] / this.leverArr[1]); var _toCol = this.imgRandomArr[to] % this.leverArr[1]; // 移动两张图片 this.imgCells[from].style.left = _toCol*this.cellWidth + 'px'; this.imgCells[from].style.top = _toRow*this.cellHeight + 'px'; this.imgCells[to].style.left = _fromCol*this.cellWidth + 'px'; this.imgCells[to].style.top = _fromRow*this.cellHeight + 'px'; // 将乱序数组中的两个值交换位置 // 定义一个临时变量来实现交换顺序 var _temp = this.imgRandomArr[from]; this.imgRandomArr[from] = this.imgRandomArr[to]; this.imgRandomArr[to] = _temp; //如果乱序数组和原数组一致,则表示拼图已完成 if(this.imgOrigArr.toString() === this.imgRandomArr.toString()){ // 调用成功方法 this.success(); } }
源码保存在了<a target = "_blank" href="https://github.com/MagicianShiro/gamePractice">github</a>上,有爱点击github自取。
在线地址可以直接测试使用,<a target = "_blank" href="http://139.199.6.43/puzzle/puzzle.html">拼图小游戏</a>←点击跳转即可
总结:
- 最近不知道怎么了,老是不在状态,脑袋反应比较慢,一个简单问题总是要想好久。感觉整个人都魔怔了。。。。
- 本来我是想自动获取图片宽高来切成行列的,不过行数列数不相等的话,好像有点麻烦。而且不是正方形也不好看啊。想了想还是不弄了。。想兴趣的朋友自己弄弄吧。。
- 顺便一提,上面的动图所用到的图片,画师是P站的 METO@三日目東ミ24a