Vue.js实现拼图游戏
Vue.js实现拼图游戏
之前写过一篇《基于Vue.js的表格分页组件》的文章,主要介绍了Vue组件的编写方法,有兴趣的可以访问这里进行阅读:http://www.cnblogs.com/luozhihao/p/5516065.html
前言
为了进一步让大家了解Vue.js的神奇魅力,了解Vue.js的一种以数据为驱动的理念,本文主要利用Vue实现了一个数字拼图游戏,其原理并不是很复杂,效果图如下:
demo展示地址为:https://luozhihao.github.io/vue-puzzle/index.html#!/
有能力的可以玩玩,拼出来有赏哦~~
功能分析
当然玩归玩,作为一名Vue爱好者,我们理应深入游戏内部,一探代码的实现。接下来我们就先来分析一下要完成这样的一个游戏,主要需要实现哪些功能。下面我就直接将此实例的功能点罗列在下了:
随机生成1~15的数字格子,每一个数字都必须出现且仅出现一次
点击一个数字方块后,如其上下左右有一处为空,则两者交换位置
格子每移动一步,我们都需要校验其是否闯关成功
点击重置游戏按钮后需对拼图进行重新排序
以上便是本实例的主要功能点,可见游戏功能并不复杂,我们只需一个个攻破就OK了,接下来我就来展示一下各个功能点的Vue代码。
构建游戏面板
作为一款以数据驱动的JS框架,Vue的HTML模板很多时候都应该绑定数据的,比如此游戏的方块格子,我们这里肯定是不能写死的,代码如下:
![](https://common.cnblogs.com/images/copycode.gif)
<template>
<div class="box">
<ul class="puzzle-wrap">
<li
:class="{'puzzle': true, 'puzzle-empty': !puzzle}"
v-for="puzzle in puzzles"
v-text="puzzle"
></li>
</ul>
</div>
</template> <script>
export default {
data () {
return {
puzzles: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
}
}
}
</script>
![](https://common.cnblogs.com/images/copycode.gif)
这里我省略了css样式部分,大家可以先不用关心。以上代码我们将1~15的数字写死在了一个数组中,这显然不是随机排序的,那么我们就来实现随机排序的功能。
随机排序数字
![](https://common.cnblogs.com/images/copycode.gif)
<template>
<div class="box">
<ul class="puzzle-wrap">
<li
:class="{'puzzle': true, 'puzzle-empty': !puzzle}"
v-for="puzzle in puzzles"
v-text="puzzle"
></li>
</ul>
</div>
</template> <script>
export default {
data () {
return {
puzzles: []
}
},
methods: { // 重置渲染
render () {
let puzzleArr = [],
i = 1 // 生成包含1 ~ 15数字的数组
for (i; i < 16; i++) {
puzzleArr.push(i)
} // 随机打乱数组
puzzleArr = puzzleArr.sort(() => {
return Math.random() - 0.5
}); // 页面显示
this.puzzles = puzzleArr
this.puzzles.push('')
},
},
ready () {
this.render()
}
}
![](https://common.cnblogs.com/images/copycode.gif)
以上代码,我们利用for循环生成了一个1~15的有序数组,之后我们又利用原生JS的sort方法随机打乱数字,这里还包含了一个知识点就是Math.random()方法。
利用sort()方法进行自定义排序,我们需要提供一个比较函数,然后返回一个用于说明这两个值的相对顺序的数字,其返回值如下:
返回一个小于 0 的值,说明 a 小于 b
返回 0,说明 a 等于 b
返回一个大于 0 的值,说明 a 大于 b
这里利用Math.random()生成一个 0 ~ 1 之间的随机数,再减去0.5,这样就会有一半概率返回一个小于 0 的值, 一半概率返回一个大于 0 的值,就保证了生成数组的随机性,实现了动态随机生成数字格子的功能。
需要注意的是,我们还在数组最后插了一个空字符串,用来生成唯一的空白格子。
交换方块位置
![](https://common.cnblogs.com/images/copycode.gif)
<template>
<div class="box">
<ul class="puzzle-wrap">
<li
:class="{'puzzle': true, 'puzzle-empty': !puzzle}"
v-for="puzzle in puzzles"
v-text="puzzle"
@click="moveFn($index)"
></li>
</ul>
</div>
</template> <script>
export default {
data () {
return {
puzzles: []
}
},
methods: { // 重置渲染
render () {
let puzzleArr = [],
i = 1 // 生成包含1 ~ 15数字的数组
for (i; i < 16; i++) {
puzzleArr.push(i)
} // 随机打乱数组
puzzleArr = puzzleArr.sort(() => {
return Math.random() - 0.5
}); // 页面显示
this.puzzles = puzzleArr
this.puzzles.push('')
}, // 点击方块
moveFn (index) { // 获取点击位置及其上下左右的值
let curNum = this.puzzles[index],
leftNum = this.puzzles[index - 1],
rightNum = this.puzzles[index + 1],
topNum = this.puzzles[index - 4],
bottomNum = this.puzzles[index + 4] // 和为空的位置交换数值
if (leftNum === '') {
this.puzzles.$set(index - 1, curNum)
this.puzzles.$set(index, '')
} else if (rightNum === '') {
this.puzzles.$set(index + 1, curNum)
this.puzzles.$set(index, '')
} else if (topNum === '') {
this.puzzles.$set(index - 4, curNum)
this.puzzles.$set(index, '')
} else if (bottomNum === '') {
this.puzzles.$set(index + 4, curNum)
this.puzzles.$set(index, '')
}
}
},
ready () {
this.render()
}
}
</script>
![](https://common.cnblogs.com/images/copycode.gif)
这里我们首先在每个格子的li上添加了点击事件@click="moveFn($index)",通过$index参数获取点击方块在数组中的位置
其次获取其上下左右的数字在数组中的index值依次为index - 4、index + 4、index - 1、index + 1
当我们找到上下左右有一处为空的时候我们将空的位置赋值上当前点击格子的数字,将当前点击的位置置为空
备注:我们为什么要使用$set方法,而不直接用等号赋值呢,这里包含了Vue响应式原理的知识点。
![](https://common.cnblogs.com/images/copycode.gif)
// 因为 JavaScript 的限制,Vue.js 不能检测到下面数组变化: // 1.直接用索引设置元素,如 vm.items[0] = {};
// 2.修改数据的长度,如 vm.items.length = 0。
// 为了解决问题 (1),Vue.js 扩展了观察数组,为它添加了一个 $set() 方法: // 与 `example1.items[0] = ...` 相同,但是能触发视图更新
example1.items.$set(0, { childMsg: 'Changed!'})
![](https://common.cnblogs.com/images/copycode.gif)
详见:http://cn.vuejs.org/guide/list.html#问题
检测是否闯关成功
![](https://common.cnblogs.com/images/copycode.gif)
<template>
<div class="box">
<ul class="puzzle-wrap">
<li
:class="{'puzzle': true, 'puzzle-empty': !puzzle}"
v-for="puzzle in puzzles"
v-text="puzzle"
@click="moveFn($index)"
></li>
</ul>
</div>
</template> <script>
export default {
data () {
return {
puzzles: []
}
},
methods: { // 重置渲染
render () {
let puzzleArr = [],
i = 1 // 生成包含1 ~ 15数字的数组
for (i; i < 16; i++) {
puzzleArr.push(i)
} // 随机打乱数组
puzzleArr = puzzleArr.sort(() => {
return Math.random() - 0.5
}); // 页面显示
this.puzzles = puzzleArr
this.puzzles.push('')
}, // 点击方块
moveFn (index) { // 获取点击位置及其上下左右的值
let curNum = this.puzzles[index],
leftNum = this.puzzles[index - 1],
rightNum = this.puzzles[index + 1],
topNum = this.puzzles[index - 4],
bottomNum = this.puzzles[index + 4] // 和为空的位置交换数值
if (leftNum === '') {
this.puzzles.$set(index - 1, curNum)
this.puzzles.$set(index, '')
} else if (rightNum === '') {
this.puzzles.$set(index + 1, curNum)
this.puzzles.$set(index, '')
} else if (topNum === '') {
this.puzzles.$set(index - 4, curNum)
this.puzzles.$set(index, '')
} else if (bottomNum === '') {
this.puzzles.$set(index + 4, curNum)
this.puzzles.$set(index, '')
} this.passFn()
}, // 校验是否过关
passFn () {
if (this.puzzles[15] === '') {
const newPuzzles = this.puzzles.slice(0, 15) const isPass = newPuzzles.every((e, i) => e === i + 1) if (isPass) {
alert ('恭喜,闯关成功!')
}
}
}
},
ready () {
this.render()
}
}
</script>
![](https://common.cnblogs.com/images/copycode.gif)
我们在moveFn方法里调用了passFn方法来进行检测,而passFn方法里又涉及了两个知识点:
(1)slice方法
通过slice方法我们截取数组的前15个元素生成一个新的数组,当然前提了数组随后一个元素为空
(2)every方法
通过every方法我们来循环截取后数组的每一个元素是否等于其index+1值,如果全部等于则返回true,只要有一个不等于则返回false
如果闯关成功那么isPass的值为true,就会alert "恭喜,闯关成功!"提示窗,如果没有则不提示。
重置游戏
重置游戏其实很简单,只需添加重置按钮并在其上调用render方法就行了:
![](https://common.cnblogs.com/images/copycode.gif)
<template>
<div class="box">
<ul class="puzzle-wrap">
<li
:class="{'puzzle': true, 'puzzle-empty': !puzzle}"
v-for="puzzle in puzzles"
v-text="puzzle"
@click="moveFn($index)"
></li>
</ul>
<button class="btn btn-warning btn-block btn-reset" @click="render">重置游戏</button>
</div>
</template> <script>
export default {
data () {
return {
puzzles: []
}
},
methods: { // 重置渲染
render () {
let puzzleArr = [],
i = 1 // 生成包含1 ~ 15数字的数组
for (i; i < 16; i++) {
puzzleArr.push(i)
} // 随机打乱数组
puzzleArr = puzzleArr.sort(() => {
return Math.random() - 0.5
}); // 页面显示
this.puzzles = puzzleArr
this.puzzles.push('')
}, // 点击方块
moveFn (index) { // 获取点击位置及其上下左右的值
let curNum = this.puzzles[index],
leftNum = this.puzzles[index - 1],
rightNum = this.puzzles[index + 1],
topNum = this.puzzles[index - 4],
bottomNum = this.puzzles[index + 4] // 和为空的位置交换数值
if (leftNum === '') {
this.puzzles.$set(index - 1, curNum)
this.puzzles.$set(index, '')
} else if (rightNum === '') {
this.puzzles.$set(index + 1, curNum)
this.puzzles.$set(index, '')
} else if (topNum === '') {
this.puzzles.$set(index - 4, curNum)
this.puzzles.$set(index, '')
} else if (bottomNum === '') {
this.puzzles.$set(index + 4, curNum)
this.puzzles.$set(index, '')
} this.passFn()
}, // 校验是否过关
passFn () {
if (this.puzzles[15] === '') {
const newPuzzles = this.puzzles.slice(0, 15) const isPass = newPuzzles.every((e, i) => e === i + 1) if (isPass) {
alert ('恭喜,闯关成功!')
}
}
}
},
ready () {
this.render()
}
}
</script> <style>
@import url('./assets/css/bootstrap.min.css'); body {
font-family: Arial, "Microsoft YaHei";
} .box {
width: 400px;
margin: 50px auto 0;
} .puzzle-wrap {
width: 400px;
height: 400px;
margin-bottom: 40px;
padding: 0;
background: #ccc;
list-style: none;
} .puzzle {
float: left;
width: 100px;
height: 100px;
font-size: 20px;
background: #f90;
text-align: center;
line-height: 100px;
border: 1px solid #ccc;
box-shadow: 1px 1px 4px;
text-shadow: 1px 1px 1px #B9B4B4;
cursor: pointer;
} .puzzle-empty {
background: #ccc;
box-shadow: inset 2px 2px 18px;
} .btn-reset {
box-shadow: inset 2px 2px 18px;
}
</style>
![](https://common.cnblogs.com/images/copycode.gif)
这里我一并加上了css代码。
总结
其实本游戏的代码量不多,功能点也不是很复杂,不过通过Vue来写这样的游戏,有助于我们了解Vue以数据驱动的响应式原理,在简化代码量的同时也增加了代码的可读性。
本实例的所有源码我已经上传至我的github,地址为https://github.com/luozhihao/vue-puzzle 需要的童鞋可以自行下载运行。
原创文章,转载请注明来自一个萝卜一个坑 -博客园[http://www.cnblogs.com/luozhihao]
本文地址:http://www.cnblogs.com/luozhihao/p/5726661.html
本文同步发表于:https://segmentfault.com/a/1190000006137236
Vue.js实现拼图游戏的更多相关文章
- 利用Vue.js实现拼图游戏
之前写过一篇<基于Vue.js的表格分页组件>的文章,主要介绍了Vue组件的编写方法,有兴趣的可以访问这里进行阅读:http://www.cnblogs.com/luozhihao/p/5 ...
- 原生JS实现拼图游戏
最近无聊,练练原生JS:实现拼图游戏.两种玩法:第一种是单击元素进行交换位置:第二种是拖拽元素进行位置交换.首先需要上传图片并进行回显(需要用到FileReader):下面是部分截图: 可以自行设置切 ...
- 【Vue.js游戏机实战】- Vue.js实现九宫格水果机抽奖游戏总结
大家好!先上图看看本次案例的整体效果. 完整版实战课程附源码:[Vue.js游戏机实战]- Vue.js实现九宫格水果机抽奖 实现思路: Vue component实现九宫格水果机组件,可以嵌套到任意 ...
- vue.js用法和特性详解
前 言 最近用Vue.js做了一个数据查询平台,还做了一个拼图游戏,突然深深的感到了vue的强大. Vue.js是一套构建用户界面(user interface)的渐进式框架.与其他重量级框架不 ...
- 【Vue.js实战案例】- Vue.js递归组件实现组织架构树和选人功能
大家好!先上图看看本次案例的整体效果. 浪奔,浪流,万里涛涛江水永不休.如果在jq时代来实这个功能简直有些噩梦了,但是自从前端思想发展到现在的以MVVM为主流的大背景下,来实现一个这样繁杂的功能简直不 ...
- 【Vue.js游戏机实战】- Vue.js实现大转盘抽奖总结
大家好!先上图看看本次案例的整体效果. 实现思路: Vue component实现大转盘组件,可以嵌套到任意要使用的页面. css3 transform控制大转盘抽奖过程的动画效果. 抽奖组件内使用钩 ...
- 【Vue.js游戏机实战】- Vue.js实现老虎-机抽奖总结
大家好!先上图看看本次案例的整体效果. 完整版实战课程附源码:[Vue.js游戏机实战]- Vue.js实现老虎-机抽奖 实现思路: Vue component实现老虎-机组件,可以嵌套到任意要使用的 ...
- 拼图游戏js
实现算法: 1. JavaScript动态生成拼图:通过生成16个div,且除最后一个div不使用背景图片以外,其他div都设置拼图图片为背景.然后通过调整background-position来实现 ...
- html+css+js实现网页拼图游戏
代码地址如下:http://www.demodashi.com/demo/14449.html 项目描述 使用 html+js+css 实现一个网页拼图游戏,可支持简单,中等,困难三种难度. 演示效果 ...
随机推荐
- py正则表达式
1.元字符 . ^ $ * + ? {} [] \ | () --> [] : - 常用来指定一个字符集:[abc], [a-z] 匹配任意一个字符 - 元字符在字符集中不起作用:[akm ...
- Qt中事件分发源代码剖析(一共8个步骤,顺序非常清楚:全局的事件过滤器,再传递给目标对象的事件过滤器,最终传递给目标对象)
Qt中事件分发源代码剖析 Qt中事件传递顺序: 在一个应该程序中,会进入一个事件循环,接受系统产生的事件,并且进行分发,这些都是在exec中进行的.下面举例说明: 1)首先看看下面一段示例代码: in ...
- Mysql.Data的连接驱动 .net 的源码竟然在git了
如标题 上链接:https://github.com/mysql/mysql-connector-net
- logstash 解析mysql slow log
# User@Host: zjzc_app[zjzc_app] @ [10.252.148.16xx] Id: 6043127 # Query_time: 2.581184 Lock_time: 0. ...
- Android原型界面设计工具
第 1 页:原型界面制作工具Lumzy 第 2 页:在线工具Mockingbird 第 3 页:开源UI工具The Pencil Project 第 4 页:JS开发工具包Dojo 第 5 ...
- 求高精度幂(java)
求高精度幂 时间限制:3000 ms | 内存限制:65535 KB 难度:2 描述 对数值很大.精度很高的数进行高精度计算是一类十分常见的问题.比如,对国债进行计算就是属于这类问题. 现在要 ...
- Android日语输入法Simeji使用示例
MainActivity如下: package cn.testsimeji; import android.os.Bundle; import android.view.View; import an ...
- Python 单词字母顺序不变且所有倒排
翻出google測试project师的一道题目: 设计一个函数,不论什么语言都能够,实现下面功能: 一个句子,将句子中的单词所有倒排过来,但单词的字母顺序不变.eg. this is a real ...
- 求斐波那契数列的第n项
问题描述:斐波那契数列是这样的一个数列,1,1,2,3,5,8,..,即前两项都是1,后面每一项都是其前面两项的和. 现在要你求出该数列的第n项. 分析:该问题是一个经典的数列问题,相信大家在很多语言 ...
- ASCII码表完整版
ASCII值 控制字符 ASCII值 控制字符 ASCII值 控制字符 ASCII值 控制字符 0 NUT 32 (space) 64 @ 96 . 1 SOH 33 ! 65 A 97 a ...