vue+canvas实现拼图小游戏


Posted in Javascript onSeptember 18, 2020

利用 vue+canvas 实现拼图小游戏,供大家参考,具体内容如下

思路步骤

  • 一个拼图拼盘和一个原图参照
  • 对原图的切割以及随机排序
  • 通过W/A/D/S或上下左右进行移动
  • 难度的自主选择
  • 对拼图是否完成的判定

JS实现部分

数据分析

  • row:拼图的总行数;column:拼图的总列数。 (用来设置拼图难度,也是每个拼图块宽高的设置规则)
  • pic[{x,y,row,column,index}]:小拼图的集合,其内元素为小拼图的数据结构。 (x、y:拼图块在canvas的绘制规则,初始化后不会进行改变;row、column:对原图进行切割并绘制的规则;index:用来判定是否完成拼图的规则之一,绘制空白块的规则,其中空白块的index=-1)
  • num:随机排列的次数。
  • sign:空白块在拼图集合 pic 中的索引。 (数字类型,用来定位空白块,跟随空白块的移动而变化,是进行移动的规则之一;默认为:15)
  • isWin:用来判断是否完成拼图的条件。 (布尔类型,默认为false)
  • step:表示移动的有效步数。 (数字类型,默认为0,重新游戏及完成游戏会清零)
  • maskShow: 编辑游戏 的判定条件。 (布尔类型,用来显示与隐藏编辑游戏的对话框,默认为false)

方法分析

拼图集合 pic 的初始化及随机排列

randomHandler() {
 // pic的初始化
 for(let i=0;i<this.row*this.column;i++) {
 // 设置切割后每个小图片的位置
 let row = parseInt(i/this.row);
 let column = i - row*this.column;
 // 对在canvas的排列进行初始化,后续不会进行改变
 let x = parseInt(i/this.row);
 let y = i - x*this.column;
 this.pic[i] = {...this.pic[i],x:x,y:y,row:row,column:column,index:i};
 // 设置最后一个元素为空白块,index = -1
 if(i == (this.row*this.column-1)) {
  this.pic[i] = {...this.pic[i],row:row,column:column,index:-1};
 }
 }
 // 随机排列 pic集合
 for(let i=0;i<this.num;i++) {
 let ran1,ran2,temp={};
 // 随机获取0-14
 ran1 = parseInt((this.row*this.column-1)*Math.random())
 ran2 = parseInt((this.row*this.column-1)*Math.random())
 temp.row = this.pic[ran1].row
 temp.column = this.pic[ran1].column
 this.pic[ran1] = {...this.pic[ran1],row:this.pic[ran2].row,column:this.pic[ran2].column}
 this.pic[ran2] = {...this.pic[ran2],...temp}
 }
}

拼图的绘制 (根据得到的随机 pic 集合进行绘制)

drawHandler() {
 // 获取 canvas DOM元素
 let canvas = this.$refs.can;
 let ctx = canvas.getContext('2d');
 canvas.width = 400;
 canvas.height = 400;
 ctx.clearRect(0,0,400,400);
 // 每个小拼图的宽高,根据canvas的宽高和拼图行数row列数column来动态设置
 // 是进行难度动态设置的唯一方式
 let width = canvas.width/this.column;
 let height = canvas.width/this.row;
 // 必须通过 Image 构造函数动态创建,若是通过获取 DOM 节点,则onload只执行一次,无法进行移动
 let img = new Image();
 img.src = require('../../public/image/test.png');
 img.onload = () => {
 for(let i=0;i<this.row*this.column;i++) {
  // 绘制到canvas的各元素的起始坐标
  let dx = this.pic[i].y * width;
  let dy = this.pic[i].x * height;
  // 对图片进行切割的起始点坐标
  let cx = this.pic[i].column * width;
  let cy = this.pic[i].row * height;
  // 参数:img图片,切割的起始点坐标,切割的宽高,绘制的起始点坐标,绘制的宽高
  ctx.drawImage(img,cx,cy,width,height,dx,dy,width,height);
  if(this.pic[i].index == -1) {
  this.sign = i;
  ctx.clearRect(dx,dy,width,height);
  }
 }
 }
}

其中 img 必须通过 Image 构造函数动态创建

拼图的移动

// 在 mounted 钩子进行键盘的监听事件
mounted() {
 this.newGame();
 document.onkeydown = (event) => {
 let key = event.keyCode;
 if(key==38 || key==87) this.moveHandler('up');
 else if (key==40 || key==83 ) this.moveHandler('down');
 else if (key==37 || key==65 ) this.moveHandler('left');
 else if (key==39 || key==68 ) this.moveHandler('right');
 }
 }
methods: {
 moveHandler(dir) {
 // re:空白块根据方向最终需移动到的位置索引
 let re,temp = {};
 if(dir == 'up' && this.pic[this.sign].x>0) {
 // 根据空白块的row和column推算出上面一块图片的序号
 // 在将两个图片快进行互换位置,及交换row、column、index
 // 重新赋值this.sign(标志着空白块的序号:默认15)
 re = (this.pic[this.sign].x-1) * this.row + this.pic[this.sign].y;
 temp.row = this.pic[re].row;
 temp.column = this.pic[re].column;
 temp.index = this.pic[re].index;
 this.pic[re] = {...this.pic[re],row:this.pic[this.sign].row,column:this.pic[this.sign].column,index:this.pic[this.sign].index};
 this.pic[this.sign] = {...this.pic[this.sign],...temp};
 this.step = this.step + 1;
 }
 else if(dir == 'down' && this.pic[this.sign].x<this.row-1) {
 re = (this.pic[this.sign].x+1) * this.row + this.pic[this.sign].y;
 temp.row = this.pic[re].row;
 temp.column = this.pic[re].column;
 temp.index = this.pic[re].index;
 this.pic[re] = {...this.pic[re],row:this.pic[this.sign].row,column:this.pic[this.sign].column,index:this.pic[this.sign].index};
 this.pic[this.sign] = {...this.pic[this.sign],...temp};
 this.step = this.step + 1;
 }
 else if(dir == 'left' && this.pic[this.sign].y>0) {
 re = (this.pic[this.sign].x) * this.row + this.pic[this.sign].y-1;
 temp.row = this.pic[re].row;
 temp.column = this.pic[re].column;
 temp.index = this.pic[re].index;
 this.pic[re] = {...this.pic[re],row:this.pic[this.sign].row,column:this.pic[this.sign].column,index:this.pic[this.sign].index};
 this.pic[this.sign] = {...this.pic[this.sign],...temp};
 this.step = this.step + 1;
 }
 else if(dir == 'right' && this.pic[this.sign].y<this.column-1) {
 re = (this.pic[this.sign].x) * this.row + this.pic[this.sign].y+1;
 temp.row = this.pic[re].row;
 temp.column = this.pic[re].column;
 temp.index = this.pic[re].index;
 this.pic[re] = {...this.pic[re],row:this.pic[this.sign].row,column:this.pic[this.sign].column,index:this.pic[this.sign].index};
 this.pic[this.sign] = {...this.pic[this.sign],...temp};
 this.step = this.step + 1;
 }
 // 重新绘制拼图,也可以通过计算只重新绘制移动的部分区域
 this.drawHandler();
 }
}

完成拼图的判定

isWinHandler() {
 // 通过比较所有元素的x、y和row、column是否相等即可,也可以通过index来判断
 for(let i=0;i<this.row*this.column;i++) {
 if(this.pic[i].x == this.pic[i].row && this.pic[i].y == this.pic[i].column) {
  // 显示成功的状态以及清空步数
  this.isWin = true;
  this.step = 0;
 }
 }
}

重新游戏

newGame() {
 // 在 mounted 钩子进行
 // 隐藏完成状态,清空步数,获取随机排列,绘制拼图模块
 this.isWin = false;
 this.step = 0;
 this.randomHandler();
 this.drawHandler();
 }

JS总合

<script>
export default {
 data() {
 return {
 // row:拼图的总行数,column:拼图的总列数
 row:2,
 column:2,
 // 随机打乱的次数
 num:100,
 // pic:拼图的所有子集和;
 // 元素:index:子图片的位置编号
 // row/column:对原图分割后的横纵编号
 // x/y:在canvas中的坐标位置(不会改变)
 pic:[{x:0,y:0,row:0,column:0,index:0}],
 sign:15,
 isWin: false,
 step:0,
 maskShow:false
 }
 },
 mounted() { 代码在拼图移动模块中 },
 methods: {
 // 判断是否完成拼图
 isWinHandler() { ... },
 // 移动的函数方法
 moveHandler(dir) { ... },
 // 绘制拼图
 drawHandler() { ... },
 // 获取随机排序
 randomHandler() { ... },
 newGame() { ... }
}
</script>

HTML部分

<template>
 <div class="index">
 <div class="contain">
 <canvas class="can" ref="can"></canvas>
 <!-- 完成拼图的状态显示 -->
 <div v-if="isWin" class="win">游戏胜利!</div>
 <div class="btns">
 <span @click="newGame">重新游戏</span>
 <span @click="maskShow = true">编辑游戏</span>
 <span @click="isWinHandler">检验</span>
 <span>{{step}}</span>
 </div>
 <!-- 点击编辑游戏的弹出框 -->
 <div v-show="maskShow" class="mask">
 行:<input type="text" v-model="row" placeholder="请输入行数">
 列:<input type="text" v-model="column" placeholder="请输入列数">
 <button @click="maskShow = false">完成</button>
 </div>
 </div>
 <img ref="img" class="img" src="../../public/image/test.png" alt="error">
 </div>
</template>

CSS部分

<style scoped>
/* 编辑的弹出框 */
.mask {
 width: 200px;
 height: 200px;
 background-color: rosybrown;
 position: absolute;
 left: 510px;
 top: 0;
}
/* 按钮样式 */
.btns > span {
 display: inline-block;
 width: 80px;
 font-size: 12px;
 height: 24px;
 text-align: center;
 line-height: 24px;
 margin-bottom: 5px;
 background-color: thistle;
 cursor: pointer;
}
/* 右侧按钮区 */
.btns {
 width: 80px;
 height: 400px;
 border: 1px solid tan;
 border-radius: 5px;
 background-origin: border-box;
 padding: 5px;
 position: absolute;
 left: 412px;
 top: 0;
}
/* 完成拼图的状态 */
.win {
 width: 402px;
 height: 402px;
 line-height: 402px;
 text-align: center;
 font: 24px;
 opacity: 0.5;
 background-color: paleturquoise;
 position: absolute;
 top: 0;
 left: 0;
}
.img {
 display: inline-block;
}
/* canvas */
.can {
 border: 1px solid teal;
}
/* canvas容器 */
.contain {
 position: relative;
}
</style>

最终的完成结果图

vue+canvas实现拼图小游戏

代码地址:拼图游戏

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Jquery显示、隐藏元素以及添加删除样式
Aug 09 Javascript
JS+CSS设置img在DIV中只显示Img垂直居中的部分
Oct 24 Javascript
通过遮罩层实现浮层DIV登录的js代码
Feb 07 Javascript
jquery实现兼容IE8的异步上传文件
Jun 15 Javascript
每天一篇javascript学习小结(Function对象)
Nov 16 Javascript
js阻止浏览器默认行为的简单实例
May 15 Javascript
jQuery Easyui 下拉树组件combotree
Dec 16 Javascript
原生JS中slice()方法和splice()区别
Mar 06 Javascript
深入了解JavaScript代码覆盖
Jun 13 Javascript
JS控制下拉列表左右选择实例代码
May 08 Javascript
jquery实现广告上下滚动效果
Mar 04 jQuery
浅谈JS的原型和原型链
Jun 04 Javascript
JavaScript 常见的继承方式汇总
Sep 17 #Javascript
JavaScript 闭包的使用场景
Sep 17 #Javascript
javascript贪吃蛇游戏设计与实现
Sep 17 #Javascript
js实现简单的随机点名器
Sep 17 #Javascript
谈谈JavaScript中的垃圾回收机制
Sep 17 #Javascript
js对象属性名驼峰式转下划线的实例代码
Sep 17 #Javascript
详细分析JavaScript中的深浅拷贝
Sep 17 #Javascript
You might like
PHP+DBM的同学录程序(4)
2006/10/09 PHP
PHP使用GIFEncoder类生成的GIF动态图片验证码
2014/07/01 PHP
php实现给图片加灰色半透明效果的方法
2014/10/20 PHP
PHP防止注入攻击实例分析
2014/11/03 PHP
浅谈php+phpStorm+xdebug配置方法
2015/09/17 PHP
PHP 数组遍历foreach语法结构及实例
2016/06/13 PHP
PHP入门教程之图像处理技巧分析
2016/09/11 PHP
PHP无限极分类函数的实现方法详解
2017/04/15 PHP
jQueryUI如何自定义组件实现代码
2010/11/14 Javascript
jquery中的 $(&quot;#jb51&quot;)与document.getElementById(&quot;jb51&quot;) 的区别
2011/07/26 Javascript
javascript使用switch case实现动态改变超级链接文字及地址
2014/12/16 Javascript
jquery预加载图片的方法
2015/05/27 Javascript
详解iframe与frame的区别
2016/01/13 Javascript
基于JavaScript实现瀑布流效果(循环渐近)
2016/01/27 Javascript
js点击返回跳转到指定页面实现过程
2020/08/20 Javascript
js实现精确到秒的日期选择器完整实例
2016/04/30 Javascript
Javascript实现汉字和拼音互转的终极方案
2016/10/19 Javascript
判断横屏竖屏(三种)
2017/02/13 Javascript
Javascript ES6中数据类型Symbol的使用详解
2017/05/02 Javascript
微信小程序实现表单校验功能
2020/03/30 Javascript
three.js实现3D影院的原理的代码分析
2017/12/18 Javascript
python编程-将Python程序转化为可执行程序[整理]
2007/04/09 Python
实用自动化运维Python脚本分享
2018/06/04 Python
python scp 批量同步文件的实现方法
2019/01/03 Python
Django REST framework 单元测试实例解析
2019/11/07 Python
浅谈PyTorch的可重复性问题(如何使实验结果可复现)
2020/02/20 Python
浅谈pandas dataframe对除数是零的处理
2020/07/20 Python
科颜氏法国官网:Kiehl’s法国
2019/08/20 全球购物
小学数学教学反思
2014/02/02 职场文书
党员党性分析材料
2014/02/17 职场文书
大专毕业生求职信
2014/07/05 职场文书
与美同行演讲稿
2014/09/13 职场文书
婚前保证书范文
2015/02/28 职场文书
导游词之青岛太清宫
2019/12/13 职场文书
基于python的matplotlib制作双Y轴图
2021/04/20 Python
利用Matlab绘制各类特殊图形的实例代码
2021/07/16 Python