HTML内容
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div class="wrap"> <div class="start"> 开始</div> <div class="right" id="app"> <!-- 在此处渲染 --> </div> </div> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.11.3/jquery.js"></script> <script src="./index.js"></script> <script> //调用 new Puzzle({ el:‘#app‘, //插槽 data:{ width:400,//宽 height:400,//高 row:4, //行数 col:4, //列数 puzzleImg:‘./1.png‘, //图片路径
}, success(){ // 拼图成功后的回调 console.log(‘拼图成功‘) } }) </script> </body> </html>
css部分
*{ margin: 0; padding: 0; } .wrap{ width: 500px; height: 500px; margin: 0 auto; position: relative; border: 1px solid black; } .start{ height: 40px; width: 80px; border:1px solid black; margin: 10px auto; text-align: center; }
js部分
Puzzle.prototype = { init(options){ this.initData(options) this.render() this.handle() }, initData(options){ //初始化数据 this.options = options; this.el = $(options.el); this.imgArea = $(‘<div class="imgArea" style="position:relative;"></div>‘); this.puzzleWidth = options.data.width; //拼图总宽度 this.puzzleHeight = options.data.height; //拼图总高度 this.startBtn = $(options.data.startBtn); // 绑定触发拼图的按钮 this.row = options.data.row; //总行数 this.col = options.data.col; //总列数 this.puzzleImg = options.data.puzzleImg; //背景图片的路径 this.callBack = options.success ; //拼图成功后的回调 //每个块的宽高 = 总宽高 / 行列 this.cellW = this.puzzleWidth / this.col; this.cellH = this.puzzleHeight / this.row; this.oriArr = []; //标准数组 this.ranArr = []; //随机数组 this.flag = true; //标准数组与随机数组之间的切换锁 }, handle(){ //功能 this.gameState() }, render(){ //渲染 var cell,template=‘‘ //行 for(var i = 0; i < this.row; i++) { //列 for (var j = 0; j <this.col; j++) { this.oriArr.push(i*this.col +j)//[0,1,2,3,4,5,6,7,8] template += `<div class=‘cell‘ style=" position: absolute; width:${this.cellW}px; height: ${this.cellH}px; top:${this.cellH * i}px; left:${this.cellW * j}px; background-position:${-this.cellW * j}px ${-this.cellH * i}px; background-image: url(${this.puzzleImg}); background-size: ${this.puzzleWidth}px ${this.puzzleHeight}px; border: 1px solid #fff; z-index: 10; transition-property: background-position; transition-duration: 300ms; transition-timing-function: ease-in-out; " ></div> </div> ` ; } } this.imgArea.html(template); this.imgArea.css({ ‘width‘:this.puzzleWidth+ ‘px‘, ‘height‘:this.puzzleHeight+‘px‘ }) this.el.append(this.imgArea); this.cell = $(‘.cell‘) }, gameState(){ var that = this; var imgCell = this.cell, imgArea = this.imgArea this.startBtn.on(‘click‘,function(){ if (that.flag) { $(this).text(‘复原‘) that.flag = false; that.randomArr(); //打乱数组 that.cellOrder(that.ranArr);//对数组进行样式排序 imgCell.on(‘mousedown‘,function(e){ var index1 = $(this).index(); //获取在标准数组中的索引值 var left = e.pageX - imgCell.eq(index1).offset().left; var top =e.pageY- imgCell.eq(index1).offset().top; $(document).on(‘mousemove‘,function(e2){ imgCell.eq(index1).css({ ‘z-index‘:‘40‘, ‘left‘:e2.pageX - left - imgArea.offset().left , ‘top‘:e2.pageY - top - imgArea.offset().top , }) }).on(‘mouseup‘,function(e3){ var left = e3.pageX - imgArea.offset().left; var top =e3.pageY - imgArea.offset().top var index2 = that.changeIndex(left,top,index1) ;//可能将要改变的位置 console.log(index1,index2,that.ranArr) if (index1 == index2) { //判断是否还在原来的位置上 that.cellReturn(index1) //索引一样 }else{ that.cellChange(index1,index2) } $(document).off(‘mousemove‘).off(‘mouseup‘); }) }) }else{ $(this).text(‘开始‘) that.flag = true; //复原 that.cellOrder(that.oriArr); $(document).off(‘mousemove‘).off(‘mouseup‘).off(‘mousedown‘); } }) }, changeIndex(x,y,index){ //与之交换的元素下标 if (x < 0 || x > this.puzzleWidth || y < 0 || y > this.puzzleHeight) { //移动出去或者移动很小 return index; //返回当前的索引 } //鼠标拖动碎片在大图范围内移动 //判断第几行 第几列 var row = Math.floor(y/this.cellH); //拖拽了多少行 var col = Math.floor(x/this.cellW); //拖拽了多少列 var l = row * this.row +col; var i =0, len = this.ranArr.length; while((i < len ) && (this.ranArr[i] != l)){ i++; } return i; }, cellReturn(index) { //飞回index的位置 var j = this.ranArr[index] % this.col ; var i =Math.floor(this.ranArr[index] /this.row); this.cell[index].style.left = this.cellW * j + ‘px‘; this.cell[index].style.top = this.cellH * i + ‘px‘; this.cell[index].style.zIndex = ‘10‘; }, randomArr(){ //获取打乱后的随机数组 //进来之前清空 this.ranArr = []; var len = this.oriArr.length; for (var i = 0; i < len; i++) { order = Math.floor(Math.random()*len); if(this.ranArr.length>0){ while($.inArray(order,this.ranArr)>-1){ order = Math.floor(Math.random()*len); } } this.ranArr.push(order) } return; }, //i *3 + j 根据元素的下标除以3取整拿到行数i 乘3取余拿到 列数j cellOrder(arr){ //将打乱后的数组进行排序 var len = arr.length; var that = this; console.log(that.cell) for (var i = 0; i < len; i++) { that.cell.eq(i).animate({ ‘left‘:arr[i] % that.row * that.cellW + ‘px‘, ‘top‘:Math.floor(arr[i] / that.col) * that.cellH + ‘px‘ },400) } }, /** * * @param {被拖动的元素在标准数组中的下标} from * @param {被交换的元素在标准数组中的下标} to */ cellChange(from,to){ const { ranArr,cellW,cellH ,cell } = this; var that = this; //改变前的位置 var fromj = ranArr[from] % that.col; var fromi =Math.floor(ranArr[from] /that.row); //要到哪个位置 var toj = ranArr[to] % that.col; var toi =Math.floor(ranArr[to] /that.row); var temp = ranArr[from]; cell.eq(from).animate({ ‘left‘: cellW * toj + ‘px‘, ‘top‘: cellH * toi + ‘px‘ },400,function(){ $(this).css(‘z-index‘,‘10‘) }); // console.log(from,to) cell.eq(to).animate({ ‘left‘: cellW * fromj + ‘px‘, ‘top‘: cellH * fromi + ‘px‘ },400,function(){ //恢复样式,更新数组 $(this).css(‘z-index‘,‘10‘); ranArr[from] = ranArr [to]; ranArr[to] =temp; that.check() }) }, check(){ //判断是否完成游戏 if(this.oriArr.toString() == this.ranArr.toString()){ // alert(‘right‘); $(‘.start‘).text(‘开始‘); this.flag = true; this.callBack(); this.cell.unbind(‘mousemove‘).unbind(‘mouseup‘).unbind(‘mousedown‘); // $(document).off(‘mousemove‘).off(‘mouseup‘).off(‘mousedown‘); } } } function Puzzle(options){ this.init(options); }
html部分
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="./js/px-rem-index.js"></script> <link rel="stylesheet" href="./tuozhuai.css"> <title>Document</title> </head> <body style="width: 15rem; margin-left: auto; margin-right: auto; position: relative;"> <div id="app"> </div> <button class=‘reset‘>复原</button> <script src="./js/tuozhuai.js"></script> <script> new Puzzle({ el:‘#app‘, //插槽 data:{ width:6,//宽 单位rem 1rem: 50px height:6,//高 row:3, //行数 col:3, //列数 puzzleImg:‘./pintu.jpg‘, // isReset:true }, success(){ // 拼图成功后的回调 console.log(‘拼图成功‘) } }) </script> </body> </html>
css部分
margin: 0; } .puzzle { position: relative; /* width: 10rem; height: 10rem; */ border: 0.04rem solid #ccc; margin-bottom: 0.3rem; } .puzzle .block { box-sizing: border-box; position: absolute; border: 0.04rem solid #ccc; } .blockPrant{ position: absolute; }
js部分
Puzzle.prototype = { init(options) { //初始化 this.initData(options) this.render(this.isReset); this.handle() }, initData(options) { //数据初始化赋值 var self = this; this.options = options; this.el = document.querySelector(options.el); this.oPuzzle = document.createElement(‘div‘); this.puzzleWidth = options.data.width; this.puzzleHeight = options.data.height; this.row = options.data.row; //总行数 this.col = options.data.col; //总列数 this.puzzleImg = options.data.puzzleImg; //背景图片的路径 this.callBack = options.success; //拼图成功后的回调 this.isReset = options.data.isReset || false; //是否不随机 //每个块的宽高 = 总宽高 / 行列 this.blockWidth = this.puzzleWidth / this.col; this.blockHeiht = this.puzzleHeight / this.row; //包含每个拼图背景的位置信息对象的数组 this.blockImgPosition = this.getBlockImgPosition(); //随机排序的 元素位置信息对象的数组 this.blockPosition = this.getBlockPosition(); setTimeout(function () { self.blockPrantS = document.querySelectorAll(‘.blockPrant‘); self.oBlockMap = self.oPuzzle.getElementsByClassName(‘block‘); }, 0) }, getBlockImgPosition() { // return 数组 包含每个拼图背景的位置信息 var arr = []; //通过行列与每块元素宽高的乘积获取背景图显示的位置在每块元素上显示的position对象 for (var i = 0; i < this.row; i++) { for (var j = 0; j < this.col; j++) { arr.push({ x: j * this.blockWidth, y: i * this.blockHeiht }) } } return arr; }, getBlockPosition: function () { //打乱每个拼图元素的位置 var newArr = []; for (var i = 0; i < this.blockImgPosition.length; i++) { newArr[i] = this.blockImgPosition[i]; } var lastEle = newArr[newArr.length - 1]; //最后一个元素的坐标; newArr.length = newArr.length - 1; //将最后一个元素清除 空出位置; newArr.sort(function () { //将每个含有背景图位置的数组随机打乱 return Math.random() - 0.5 }) newArr.push(lastEle); return newArr; }, render(isReset) { //渲染节点 var template = ‘‘; for (var i = 0; i < this.blockPosition.length; i++) { if (isReset) { //img位置 var imgX = this.blockImgPosition[i].x; var imgY = this.blockImgPosition[i].y; } else { //img位置 var imgX = this.blockPosition[i].x; var imgY = this.blockPosition[i].y; } //元素位置 var positionX = this.blockImgPosition[i].x; var positionY = this.blockImgPosition[i].y; template += ` <div class=‘blockPrant‘ style=" width:${this.blockWidth}rem; height: ${this.blockHeiht}rem; top:${positionY}rem; left: ${positionX}rem; " > <div class=‘block‘ style=" width:${this.blockWidth}rem; height: ${this.blockHeiht}rem; background-image: url(${this.puzzleImg}); background-position:${-imgX}rem ${-imgY}rem; background-size:${this.puzzleWidth}rem ${this.puzzleHeight}rem; " draggable="true" ></div> </div> ` //追加元素 this.oPuzzle.innerHTML = template; this.oPuzzle.style.width = this.puzzleWidth + ‘rem‘; this.oPuzzle.style.height = this.puzzleHeight + ‘rem‘; this.oPuzzle.setAttribute(‘class‘, ‘puzzle‘); this.el.appendChild(this.oPuzzle); } }, handle() { //行为 var self = this; this.resetImg() // 兼容性问题,目前版本的Firefox不支持 var lis = document.querySelectorAll(".block"); var from, to, fromPosi, toPosi; var blockPrantS = document.querySelectorAll(‘.blockPrant‘); for (var i = 0; i < lis.length; i++) { lis[i].setAttribute("draggable", "true"); lis[i].ondragstart = function (event) { from = event.target || event.srcElement; //起点元素 var x = from.style.backgroundPositionX; var y = from.style.backgroundPositionY; fromPosi = { x, y } } blockPrantS[i].ondragenter = function (e) { //拖拽的目标地点元素 to = e.target; var x = to.style.backgroundPositionX; var y = to.style.backgroundPositionY; toPosi = { x, y } } lis[i].ondragend = function (e) { //交换位置 self.moveBlock(to, from, fromPosi, toPosi); //判断是否完成 self.checkWin(); } } }, moveBlock(to, from, fromPosi, toPosi) { //交换位置逻辑 将点击元素与空白元素 top 和 left 互换 to.style.backgroundPositionX = fromPosi.x; to.style.backgroundPositionY = fromPosi.y; from.style.backgroundPositionX = toPosi.x; from.style.backgroundPositionY = toPosi.y; }, checkWin() { //将标准数组中的top 和 left 与 当前的backgroundPosition 的x y 进行对比 var isWin = true; for (var i = 0; i < this.oBlockMap.length; i++) { var oBlock = this.oBlockMap[i]; var blockPrantS = this.blockPrantS[i] //top left var blockLeft = parseInt(‘-‘ + blockPrantS.style.left); var blockTop = parseInt(‘-‘ + blockPrantS.style.top); // x y var imgLeft = parseInt(oBlock.style.backgroundPositionX); var imgTop = parseInt(oBlock.style.backgroundPositionY); console.log(blockLeft, imgLeft) if (!(imgLeft == blockLeft && imgTop == blockTop)) { isWin = false; break; } } if (isWin) { this.GameWin(this.callBack) } }, GameWin(callBack) { //拼图成功后 拼图无法点击 空白区域显示 触发成功后的回调 for (var i = 0; i < this.oBlockMap.length; i++) { this.oBlockMap[i].ondragstart = null; this.oBlockMap[i].ondragend = null; this.oBlockMap[i].setAttribute("draggable", "false"); } callBack() }, resetImg() { //复原 var self = this; document.querySelector(‘.reset‘).onclick = function () { self.el.innerHTML = ‘‘; self.options.data.isReset = true self.init(self.options) } } } function Puzzle(options) { this.init(options); }
html部分
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="./js/px-rem-index.js"></script> <link rel="stylesheet" href="./index.css"> <title>Document</title> </head> <body style="width: 15rem; margin-left: auto; margin-right: auto; position: relative;"> <div id="app"> </div> <button class=‘reset‘>复原</button> <script src="./js/index.js"></script> <script> new Puzzle({ el:‘#app‘, //插槽 data:{ width:10,//宽 单位rem 按照1rem = 50px的比例换算的 height:10,//高 row:3, //行数 col:3, //列数 puzzleImg:‘./pintu.jpg‘, //图片路径 // isReset:true }, success(){ // 拼图成功后的回调 console.log(‘拼图成功‘) } }) </script> </body> </html>
css部分
* { padding: 0; margin: 0; } .puzzle { position: relative; width: 10rem; height: 10rem; border: 0.04rem solid #ccc; margin-bottom: 0.3rem; } .puzzle .block { box-sizing: border-box; position: absolute; border: 0.04rem solid #ccc; }
js部分
Puzzle.prototype = { init (options){ //初始化 this.initData(options) this.render(this.isReset) ; console.log(this.isReset) this.handle() }, initData(options){ //数据初始化赋值 var self = this; this.options = options; this.el = document.querySelector(options.el); this.oPuzzle = document.createElement(‘div‘); this.puzzleWidth = options.data.width; this.puzzleHeight = options.data.height; this.row = options.data.row; //总行数 this.col = options.data.col; //总列数 this.puzzleImg = options.data.puzzleImg; //背景图片的路径 this.callBack = options.success ; //拼图成功后的回调 this.isReset = options.data.isReset || false ; //是否不随机 //每个块的宽高 = 总宽高 / 行列 this.blockWidth = this.puzzleWidth / this.col; this.blockHeiht = this.puzzleHeight / this.row; //包含每个拼图背景的位置信息对象的数组 this.blockImgPosition = this.getBlockImgPosition(); //随机排序的 元素位置信息对象的数组 this.blockPosition = this.getBlockPosition(); setTimeout(function(){ self.oEmptyBlock = self.oPuzzle.querySelector(‘div[ref=empty]‘); self.oBlockMap = self.oPuzzle.querySelectorAll(‘div[ref=block]‘); },0) }, getBlockImgPosition(){ // return 数组 包含每个拼图背景的位置信息 var arr= []; //通过行列与每块元素宽高的乘积获取背景图显示的位置在每块元素上显示的position对象 for (var i = 0; i < this.row; i++) { for (var j = 0; j < this.col; j++) { arr.push({ x : j*this.blockWidth, y : i*this.blockHeiht }) } } return arr ; }, getBlockPosition:function(){ //打乱每个拼图元素的位置 并 空出一个没有背景的元素 var newArr = []; for (var i = 0; i < this.blockImgPosition.length; i++) { newArr[i] = this.blockImgPosition[i]; } var lastEle = newArr[newArr.length-1]; //最后一个元素的坐标; newArr.length = newArr.length - 1 ; //将最后一个元素清除 空出位置; newArr.sort(function(){ //将每个含有背景图位置的数组随机打乱 return Math.random()-0.5 }) newArr.push(lastEle); return newArr; }, render(isReset){ //渲染节点 var template = ‘‘; for (var i = 0; i < this.blockPosition.length; i++) { if (isReset) { //img位置 var imgX = this.blockImgPosition[i].x; var imgY = this.blockImgPosition[i].y; }else{ //img位置 var imgX = this.blockPosition[i].x; var imgY = this.blockPosition[i].y; } //元素位置 var positionX = this.blockImgPosition[i].x; var positionY = this.blockImgPosition[i].y; //判断是否为最后一块元素 var isLastBlock = i == this.blockImgPosition.length - 1; template += ` <div class=‘block‘ style=" width:${this.blockWidth}rem; height: ${this.blockHeiht}rem; top:${positionY}rem; left: ${positionX}rem; background-image: url(${this.puzzleImg}); background-position:${-imgX}rem ${-imgY}rem; opacity: ${isLastBlock ? 0 : 1} " ref="${isLastBlock ? ‘empty‘: ‘block‘}" ></div> ` //追加元素 this.oPuzzle.innerHTML = template; this.oPuzzle.style.width = this.puzzleWidth; this.oPuzzle.style.height = this.puzzleHeight; this.oPuzzle.setAttribute(‘class‘,‘puzzle‘); this.el.appendChild(this.oPuzzle); } }, handle(){ //行为 var self = this; this.resetImg() this.oPuzzle.onclick = function(e){ var dom = e.target; // 通过判断点击的元素是否含有 ‘block‘ 是否为空白元素 var isBlock = dom.classList.contains(‘block‘) && dom.getAttribute(‘ref‘) === ‘block‘; if (isBlock) { self.handleBlock(dom) } } }, handleBlock(dom){ //点击元素后执行 var canMove = this.checkMove(dom); //是否满足交换条件 if(!canMove) return;// 不满足交换条件 //交换位置函数 this.moveBlock(dom); // 判断是否胜利 this.checkWin(); }, checkMove(dom){ //获取当前元素的行和列 var blockRow = this.getEleIndex(dom).row; var blockCol = this.getEleIndex(dom).col; //获取空白元素的行和列 var emptyRow = this.getEleIndex(this.oEmptyBlock).row; var emptyCol = this.getEleIndex(this.oEmptyBlock).col; //检查是否可以和空白元素交换 //行数与空白元素相差一行 且列相同 或者 列数相差一列 行相同 则满足与空白元素交换条件 return blockRow === emptyRow && Math.abs(blockCol - emptyCol) === 1 || blockCol === emptyCol && Math.abs(blockRow - emptyRow) === 1 }, getEleIndex(dom){//获取点击元素的行和列 var left = dom.offsetLeft/50; var top = dom.offsetTop/50; var row = Math.round(top / this.blockHeiht); var col = Math.round(left / this.blockWidth); return { row, col } }, moveBlock(oBlock){ //交换位置逻辑 将点击元素与空白元素 top 和 left 互换 // 获取block元素的left值和top值 var blockLeft = oBlock.style.left; var blockTop = oBlock.style.top; // 设置block元素的left和top为empty元素的left和top oBlock.style.left = this.oEmptyBlock.style.left; oBlock.style.top = this.oEmptyBlock.style.top; // 设置empty元素的left和top为block元素的left和top this.oEmptyBlock.style.left = blockLeft; this.oEmptyBlock.style.top = blockTop; }, checkWin(){ //将标准数组中的top 和 left 与 当前的backgroundPosition 的x y 进行对比 var isWin = true; for (var i = 0; i < this.oBlockMap.length; i++) { var oBlock = this.oBlockMap[i]; //top left var blockLeft = parseInt(‘-‘+oBlock.style.left); var blockTop = parseInt(‘-‘+oBlock.style.top); // x y var imgLeft = parseInt(oBlock.style.backgroundPositionX); var imgTop = parseInt(oBlock.style.backgroundPositionY); if (!(imgLeft == blockLeft && imgTop == blockTop)) { isWin=false; break; } } if (isWin) { this.GameWin(this.callBack) } }, GameWin(callBack){ //拼图成功后 拼图无法点击 空白区域显示 触发成功后的回调 this.oPuzzle.onclick = null; this.oEmptyBlock.style.opacity = 1; callBack() }, resetImg(){ //复原 var self = this ; document.querySelector(‘.reset‘).onclick = function(){ self.el.innerHTML=‘‘; self.options.data.isReset =true self.init(self.options) } } } function Puzzle (options) { this.init(options); }
原文:https://www.cnblogs.com/wxyblog/p/13215198.html