应用Vue.js完成拼图游戏

之前写过一篇《基于Vue.js的表格分页组件》的文章,重要引见了Vue组件的编写要领,有兴致的能够接见这里举行浏览:https://segmentfault.com/a/11…

媒介

为了进一步让人人相识Vue.js的奇异魅力,相识Vue.js的一种以数据为驱动的理念,本文重要应用Vue完成了一个数字拼图游戏,其道理并非很庞杂,效果图以下:

《应用Vue.js完成拼图游戏》

demo展现地点为:https://luozhihao.github.io/v…!/

有才能的能够玩玩,拼出来有赏哦~~

功用剖析

固然玩归玩,作为一位Vue爱好者,我们理应深切游戏内部,一探代码的完成。接下来我们就先来剖析一下要完成如许的一个游戏,重要须要完成哪些功用。下面我就直接将此实例的功用点排列在下了:

  • 随机天生1~15的数字格子,每一个数字都必须涌现且仅涌现一次

  • 点击一个数字方块后,如其上下左右有一处为空,则二者交流位置

  • 格子每挪动一步,我们都须要校验其是不是闯关胜利

  • 点击重置游戏按钮后需对拼图举行从新排序

以上就是本实例的重要功用点,可见游戏功用并不庞杂,我们只需一个个攻破就OK了,接下来我就来展现一下各个功用点的Vue代码。

构建游戏面板

作为一款以数据驱动的JS框架,Vue的HTML模板许多时刻都应该绑定数据的,比云云游戏的方块格子,我们这里肯定是不能写死的,代码以下:

<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>

这里我省略了css款式部份,人人能够先不必体贴。以上代码我们将1~15的数字写死在了一个数组中,这明显不是随机排序的,那末我们就来完成随机排序的功用。

随机排序数字

<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()
    }
}

以上代码,我们应用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 的值,就保证了天生数组的随机性,完成了动态随机天生数字格子的功用。

须要注重的是,我们还在数组末了插了一个空字符串,用来天生唯一的空缺格子。

交流方块位置

<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 === '' && index % 4) {
                this.puzzles.$set(index - 1, curNum)
                this.puzzles.$set(index, '')
            } else if (rightNum === '' && 3 !== index % 4) {
                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>
  1. 这里我们首先在每一个格子的li上增加了点击事宜@click=”moveFn($index)”,经由过程$index参数猎取点击方块在数组中的位置

  2. 其次猎取其上下左右的数字在数组中的index值依次为index – 4、index + 4、index – 1、index + 1

  3. 当我们找到上下左右有一处为空的时刻我们将空的位置赋值上当前点击格子的数字,将当前点击的位置置为空

备注:我们为何要运用$set要领,而不直接用等号赋值呢,这里包括了Vue相应式道理的知识点。

// 由于 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!'})

详见:http://cn.vuejs.org/guide/lis…题目

检测是不是闯关胜利

<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 === '' && index % 4) {
                this.puzzles.$set(index - 1, curNum)
                this.puzzles.$set(index, '')
            } else if (rightNum === '' && 3 !== index % 4) {
                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>

我们在moveFn要领里挪用了passFn要领来举行检测,而passFn要领里又触及了两个知识点:
(1)slice要领

经由过程slice要领我们截取数组的前15个元素天生一个新的数组,固然条件是数组随后一个元素为空

(2)every要领

经由过程every要领我们来轮回截取后数组的每一个元素是不是即是其index+1值,假如悉数即是则返回true,只需有一个不即是则返回false

假如闯关胜利那末isPass的值为true,就会alert “祝贺,闯关胜利!”提醒窗,假如没有则不提醒。

重置游戏

重置游戏实在很简单,只需增加重置按钮并在其上挪用render要领就好了:

<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 === '' && index % 4) {
                this.puzzles.$set(index - 1, curNum)
                this.puzzles.$set(index, '')
            } else if (rightNum === '' && 3 !== index % 4) {
                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>

这里我一并加上了css代码。

总结

实在本游戏的代码量不多,功用点也不是很庞杂,不过经由过程Vue来写如许的游戏,有助于我们相识Vue以数据驱动的相应式道理,在简化代码量的同时也增加了代码的可读性。

本实例的一切源码我已上传至我的github,地点为https://github.com/luozhihao/… 须要的童鞋能够自行下载运转。

本文地点:https://segmentfault.com/a/11…
博客园:http://www.cnblogs.com/luozhi…

    原文作者:劳卜
    原文地址: https://segmentfault.com/a/1190000006137236
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞