Vue数字华容道
vagg 人气:0前言
恰逢春之四月,天气忽热忽凉,遇游戏大赛,以笨拙之技,书一篇小文。
游戏规则:存在n*n的格子,需要将它们按数字顺序或图片顺序一一还原即可。
环境
主要环境:
vue3 version:3.2.4
vite version:2.5.0
vue-router version:4.0.14
注:这个游戏的路由使用的是自动路由插件
主要插件:
windicss version:3.5.1
运行如图:
思路
- 搭建环境,下载依赖
- 运行项目
- 利用windicss主体兼容pc和移动端
姑且认为小于1024的是平板或者手机 lg(1024px)
App.vue
<div class="relative w-full h-full lg:(w-750px h-800px)"> <route-view </div>
主体Game.vue设置4个主体组件:GameTool.vue游戏工具栏(返回、开始和步数统计)、GameCnt.vue游戏主体、Tip.vue开局提示、GamePass.vue游戏通过
实现
GameCnt
布局
- 先画出3*3的格子,这里有多种方法,笔者这里采取最简单的动态grid布局实现,后来因为css动画选取的是transform则不用gird布局了
- 宽高获取,这里要获取,原因是使用了transform位移动画,则需要平移距离和宽高了
- 设置lazyShow,让第一次渲染不会有transform动画
- 隐藏最后一个,利用数组对象value值+css实现
- 添加其他css(windicss不好实现的css)
// 定义行个数 const rowLen = 3 // 定义cnt宽高和item的宽高 const cntWidth = ref(0) const cntHeight = ref(0) const itemWidth = ref(0) const itemHeight = ref(0) // 定义数组 const lists = ref([]) lists.value = new Array(rowLen.value * rowLen.value).fill(1).map((item, index) => ({ key: index, // 存储原序号 value: item, // 1 代表不是空位 moveIndex: index })) // 设置最后一个为-1 lists.value[lists.value.length - 1]['value'] = 0 //获取dom和渲染 onMounted(() => { // 获取cnt宽高和item的宽高 getCntWidth() // 让第一次渲染不会有transform动画 lazyShow.value = false })
<div v-show="!lazyShow" v-for="(item, index ) in lists" class="box rounded-md overflow-hidden absolute" :class="[item.value ? 'origin' : 'opacity-0']" @click="boxClick(item)" :style="{ transform: `translate(${(item.moveIndex % rowLen) * (1 / rowLen) * cntWidth}px, ${parseInt(item.moveIndex / rowLen) * (1 / rowLen) * cntHeight}px) `, width: itemWidth + 'px', height: itemHeight + 'px' }"> <p class="absolute z-10 text-light-100 left-1/2 top-1/2" :class="hasImg ? 'opacity-60' : ''" :style="{ 'font-size': (180 / rowLen) + 'px' }">{{ item.key + 1 }}</p> </div>
点击元素的交换
核心代码:
// 是否在一行 const isInline = parseInt(index / rowLen.value) === parseInt(emptyIndex / rowLen.value) // 在一行是否相邻 Math.abs(emptyIndex - index) === 1 // 不在一行是否是上下关系 Math.abs(index - emptyIndex) === rowLen.value
点击元素上下左右的交换
先判断是否在一行,再判断是否相邻即可。
- 先判断是否在一行
- 在一行是否相邻或不在一行是否是上下关系
- 看情况调用changeIndex
// 是否在一行 if (isInline) { // 一行则判断是否左右相邻 console.log('相差:' + (index - emptyIndex)) // 是否相邻 if (Math.abs(emptyIndex - index) === 1) { // 改变对应moveIndex changeIndex() } else { console.log('不相邻') return } } else { // 不是则判断是否上下相邻 console.log('相差:' + (index - emptyIndex)) // 是否上或者下 if (Math.abs(index - emptyIndex) === rowLen.value) { // 改变对应moveIndex changeIndex() } else { console.log('不相邻') return } } // 声明改变的数组moveIndex的方法 const changeIndex = () => { // 步数改变 emit('stepChange'); // 改变对应数组里的moveIndex 注意不是 index和 moveIndex ([lists.value[item.key].moveIndex, lists.value[emptyItem.key].moveIndex] = [emptyIndex, index]); }
判断游戏通过
这个地方很简单,主要判断数组对象每一个是否原序号key是否都相等于移动后的moveIndex
// 是否都成功 const getIsAllOk = () => { // return lists.value.some(item => item.moveIndex !== item.key) } // // 判断是否都成功了 const flag = getIsAllOk() console.log(flag) if (!flag) { // alert('成功') gamePass() }
注:这个gamePass
需要在defineEmits里注册
打乱数组
这个地方很有点麻烦,存在无解的情况,暂时没想到更好的实现方法,这里使用的是排序打乱
// 打乱数据 const mixData = () => { const arr = new Array(rowLen.value * rowLen.value).fill(0).map((item, index) => index) // console.log(arr) arr.sort(() => { return Math.random() > 0.5 ? -1 : 1 }) arr.forEach((item, index) => { lists.value[index].moveIndex = item }) // 如果直接是成功的则重新来一次排序 const flag = getIsAllOk() if (!flag) { // alert('成功') mixData() } }
注:有想法可以沟通下,一起提升!
动态格子和宽高
const params = route.query // 定义行个数 rowLen.value = +params.num || 3; // 重新获取item宽高 cnt.value && getCntWidth()
图片华容道
图片使用定位。然后超出截取即可展示出效果
<img v-if="hasImg" class="absolute" :src="Default" alt="" :style="{ width: cntWidth + 'px', maxWidth: cntWidth + 'px', height: cntHeight + 'px', left: -(item.key % rowLen) * (1 / rowLen) * cntWidth + 'px', top: -parseInt(item.key / rowLen) * (1 / rowLen) * cntHeight + 'px' }"> // 是否渲染图片 hasImg.value = params.hasImg === '1' ? true : false;
GameTool
左侧是返回按钮,右侧是重新开始按钮,中间是移动步数
移动步数后中间会调整为移动多少次
注:图标从iconfont找的
GamePass
- 先布局全屏遮罩
- 设置中间div样式
- 设置通过成功提示和步数提示即可
GameTip
这个组件就是开局的提示组件
Menu
这个组件就是菜单页 设置了两种入口
最后
GameCnt 可以写一些测试代码,方便测试下通关后的情况
GameCnt 的块级效果优化过,否则只有颜色,不太好看
GamePass 还有优化空间,例如图片通过可以有不同效果啊等等
整体难度不高,可以练习下vue3,以及游戏思维
加载全部内容