• vue3版本网页小游戏


    目录

    1.前言

    2.实现过程

    2.1目录

    2.2文件介绍

    3.核心逻辑分步骤详解

    4.总结


    1.前言

    最近火爆全网的羊了个羊小程序,背景是根据官方介绍,“羊了个羊”是一款闯关消除小游戏,通关率不到0.1%。主要玩法为重叠的各类方块,需要在下方7个栏内完成消除(3个同类消除),其特点就是“极难”,也因此成为热门挑战。我也颇感兴趣,去玩了2把,的确很有乐趣,整理了一下思路,决定搞个vue3版本的网页版本,我看网上有react版本的了,vue3版本还没有,下面分别给出设计思路,实现方式,和玩法

    设计思路:

    1,先来一张背景图,网上搜一张草地图片

    2,最底部设置七个槽位,有三个连续相同的就消除,槽位满了的话,挑战失败

    3,中间的图层区域使用重叠的方式,可能是半重叠,可能是全重叠,只有第一层可以移入槽位,全部消除时,表示挑战成功!后续挑战是变化关卡的布局方式(多种排列方式)

    4,点击事件的思路(内层不能点击,前置点击如果槽位满了还没有消除完,关卡的消除,消除动作 和 添加爆炸效果,进入下一关,挑战失败)

    5,辅助类函数:判断是否过关,消除函数,实现爆炸💥效果,控制关卡

    实现方式:

    vue3配合pinia实现数据驱动页面

    玩法:

    使用关卡模式,从第1关简单到2困难,3关复杂,这里的关卡只是数据的多少变化而已,可以设计出无数关卡,这里前端模拟json数据,使用对象json

    效果演示:

    在线体验 :

    KinHKinhttps://rondsjinhuajin.github.io/DemoVue/#/

    源码地址:

    github欢迎follow和star,感谢可爱的各位看官大佬~❤️

    2.实现过程

    2.1目录

    2.2文件介绍

     入口文件index.vue,设计背景色

    1. <script setup lang='ts'>
    2. import Header from "./components/Header.vue";
    3. import Main from "./components/Main.vue";
    4. </script>
    5. <template>
    6. <div class="sheep-wrap">
    7. <div class="sheep">
    8. <div class="sheep-wrap">
    9. <div class="sheep">
    10. <Header />
    11. <Main />
    12. </div>
    13. </div>
    14. </div>
    15. </div>
    16. </template>
    17. <style scoped lang='less'>
    18. .sheep-wrap {
    19. .sheep {
    20. display: flex;
    21. flex-direction: column;
    22. height: 100%;
    23. width: 100%;
    24. padding-bottom: 20px;
    25. }
    26. width: 100%;
    27. height: calc(100vh - 60px);
    28. background: url("../../assets/images/sheep.png") center no-repeat;
    29. background-size: cover;
    30. }
    31. </style>

    Header.vue文件,文字动效,配合pinia显示第几关

    1. <script lang='ts' setup>
    2. import { useSheepStore } from "@/stores/sheep";
    3. const store = useSheepStore();
    4. </script>
    5. <template>
    6. <div class="sheep-header">
    7. <div>第{{ store.step + 1 }}关</div>
    8. <div>
    9. <span class="l">羊了个羊🐑vue3版本</span
    10. ><span
    11. style="font-size: 14px;font-family: 'Times New Roman', Times, serif';"
    12. >(KinHKin)</span
    13. >
    14. </div>
    15. </div>
    16. </template>
    17. <style scoped lang="less">
    18. .flex-center {
    19. display: flex;
    20. align-items: center;
    21. }
    22. .sheep-header {
    23. padding-top: 2rem;
    24. text-align: center;
    25. letter-spacing: 0.2rem;
    26. font-size: 1.5rem;
    27. color: #fff;
    28. border-bottom: 1px solid #1d9614;
    29. padding-bottom: 1rem;
    30. margin-bottom: 2rem;
    31. div .l {
    32. background-image: -webkit-linear-gradient(
    33. left,
    34. #1d9614,
    35. #fff 25%,
    36. #666 50%,
    37. #e6d205 75%,
    38. #fff
    39. );
    40. -webkit-text-fill-color: transparent;
    41. -webkit-background-clip: text;
    42. -webkit-background-size: 200% 100%;
    43. -webkit-animation: maskedAnimation 4s infinite linear;
    44. padding-right: 8px;
    45. }
    46. }
    47. @keyframes maskedAnimation {
    48. 0% {
    49. background-position: 0 0;
    50. }
    51. 100% {
    52. background-position: -100% 0;
    53. }
    54. }
    55. </style>

    Main.vue文件是核心文件,作用是引入颜色,控制关卡,设置关卡数据,如何消除,增加爆炸动效,控制交互逻辑等。

    1. <script setup lang="ts">
    2. import { ref, type Ref } from "vue";
    3. import { ElMessage, ElMessageBox } from "element-plus";
    4. import { useSheepStore } from "@/stores/sheep";
    5. // 关卡数据
    6. import data from "./data.json";
    7. // 颜色
    8. import constants from "./constants";
    9. // pinia 控制关卡
    10. const store = useSheepStore();
    11. // 七个槽位
    12. // const footerList = ref([0, 1, 2, 3, 4, 5, 6]);
    13. const footerList: Ref<Array<any> | [any]> = ref([]);
    14. const colors = ref(constants.colors);
    15. // 关卡响应式
    16. const totalList: Ref<Array<any> | [any]> = ref([]);
    17. totalList.value = data["list1"]; // 默认第一关
    18. // 控制动画效果结束才能点击
    19. const isNotClick = ref(false);
    20. // 点击控制事件
    21. function handleClick(
    22. i: number,
    23. k: number,
    24. onei: { oneSub: string | Array<string> },
    25. onek: number,
    26. oneiSub: Array<number>,
    27. onekSub: number
    28. ) {
    29. console.log(i, k, onei, onek, oneiSub, onekSub, "测试");
    30. if (isNotClick.value) {
    31. return false;
    32. }
    33. // 内层不能点击
    34. if (onekSub !== onei.oneSub.length - 1) {
    35. return false;
    36. }
    37. // 前置点击如果槽位满了还没有消除完
    38. fullFun()
    39. // 关卡的消除
    40. let tempList = fixFun(k, onekSub, onek, oneiSub)
    41. // 消除动作 和 添加爆炸效果
    42. if (footerList.value.length > 2) {
    43. isNotClick.value = true
    44. const { list, flag } = eliminationFunction(footerList.value)
    45. footerList.value = list;
    46. if (flag) {
    47. footerList.value = addBoomFunction(footerList.value);
    48. }
    49. setTimeout(() => {
    50. const { list, flag } = eliminationFunction(footerList.value)
    51. footerList.value = list;
    52. isNotClick.value = false
    53. }, 1000);
    54. // 进入下一关
    55. nextFun(tempList)
    56. }
    57. // 挑战失败
    58. failFun(tempList)
    59. console.log(footerList, tempList, "tempList");
    60. }
    61. // full
    62. function fullFun() {
    63. if (footerList.value.length === 7) {
    64. ElMessage.closeAll();
    65. ElMessageBox.alert("挑战失败,点击确定返回!", "Warning", {
    66. confirmButtonText: "确定",
    67. type: "warning",
    68. showClose: false,
    69. }).then(() => {
    70. location.reload();
    71. });
    72. return false;
    73. }
    74. }
    75. // fix
    76. function fixFun(k: number, onekSub: number, onek: number, oneiSub: Array<number>) {
    77. const { value } = totalList;
    78. let tempList = JSON.parse(JSON.stringify(value));
    79. for (let i = 0; i < tempList.length; i++) {
    80. const one = tempList[k].one;
    81. for (let j = 0; j < one.length; j++) {
    82. const oneSub = one[onek];
    83. for (let k = 0; k < oneSub.oneSub.length; k++) {
    84. if (onekSub === k) {
    85. const footItem = oneSub.oneSub.splice(onekSub);
    86. break;
    87. }
    88. }
    89. }
    90. }
    91. footerList.value.push(oneiSub);
    92. totalList.value = tempList;
    93. return tempList
    94. }
    95. //fail
    96. function failFun(tempList: any[]) {
    97. setTimeout(() => {
    98. if (footerList.value.length > 0 && !jugeList(tempList)) {
    99. ElMessage.closeAll();
    100. ElMessageBox.alert("挑战失败,点击确定返回!", "Warning", {
    101. confirmButtonText: "确定",
    102. type: "warning",
    103. showClose: false,
    104. }).then(() => {
    105. location.reload();
    106. });
    107. return false;
    108. }
    109. }, 1002)
    110. }
    111. // next
    112. function nextFun(tempList: any[]) {
    113. setTimeout(() => {
    114. if (!footerList.value.length && !jugeList(tempList)) {
    115. // debugger
    116. ElMessage.closeAll();
    117. ElMessage.success("恭喜您,挑战成功!进入下一关");
    118. store.step++;
    119. const inStep: string = "list" + (store.step + 1);
    120. totalList.value = JSON.parse(JSON.stringify(data))[inStep];
    121. footerList.value = [];
    122. }
    123. }, 1001)
    124. }
    125. // 判断是否过关
    126. function jugeList(list: any[]) {
    127. let temp: any = [];
    128. list?.forEach((oeni: { one: any }) => {
    129. oeni?.one?.forEach((sub: { oneSub: any }) => {
    130. temp = [...temp, ...sub.oneSub];
    131. });
    132. });
    133. return temp.length;
    134. }
    135. // 消除函数
    136. function eliminationFunction(list: any[]) {
    137. let flag: boolean = false;
    138. for (let k = 0; k < list.length - 2; k++) {
    139. const temp = list;
    140. const arr = temp.slice(k, k + 3);
    141. console.log(k, arr);
    142. if (arr[0] === arr[1] && arr[1] === arr[2] && arr[0] === arr[2]) {
    143. list.splice(k + 2);
    144. list.splice(k + 1);
    145. list.splice(k, 1);
    146. flag = true
    147. break;
    148. }
    149. }
    150. return { list, flag };
    151. }
    152. // 实现爆炸💥效果
    153. function addBoomFunction(list: any[]) {
    154. const temp = JSON.parse(JSON.stringify([...list, ...['boom', 'boom', 'boom']]))
    155. return temp;
    156. }
    157. </script>
    158. <template>
    159. <div class="sheep-main">
    160. <div class="sheep-main-wrap">
    161. <template v-for="(i, k) in totalList" :key="'i' + k">
    162. <el-row v-if="i.one">
    163. <el-col :span="8" v-for="(onei, onek) in i.one" :key="'i' + onek">
    164. <div class="pic-list">
    165. <div class="pic-list-item" v-for="(oneiSub, onekSub) in onei.oneSub"
    166. :style="!onei.full ? `--i:${onekSub}` : `--i:0`"
    167. :class="onei.full && onei.oneSub.length > 1 ? 'true' : ''" :key="'i' + onekSub"
    168. @click="handleClick(i, k, onei, onek, oneiSub, onekSub)">
    169. <el-icon class="fz" v-if="oneiSub === 0">
    170. <StarFilled :color="colors[0]" />
    171. </el-icon>
    172. <el-icon class="fz" v-if="oneiSub === 1">
    173. <Aim :color="colors[1]" />
    174. </el-icon>
    175. <el-icon class="fz" v-if="oneiSub === 2">
    176. <Grid :color="colors[2]" />
    177. </el-icon>
    178. <el-icon class="fz" v-if="oneiSub === 3">
    179. <HelpFilled :color="colors[3]" />
    180. </el-icon>
    181. <el-icon class="fz" v-if="oneiSub === 4">
    182. <Star :color="colors[4]" />
    183. </el-icon>
    184. <el-icon class="fz" v-if="oneiSub === 5">
    185. <Menu :color="colors[5]" />
    186. </el-icon>
    187. <el-icon class="fz" v-if="oneiSub === 6">
    188. <Camera :color="colors[6]" />
    189. </el-icon>
    190. <el-icon class="fz" v-if="oneiSub === 7">
    191. <Bicycle :color="colors[7]" />
    192. </el-icon>
    193. <el-icon class="fz" v-if="oneiSub === 8">
    194. <IceTea :color="colors[8]" />
    195. </el-icon>
    196. <el-icon class="fz" v-if="oneiSub === 9">
    197. <ColdDrink :color="colors[9]" />
    198. </el-icon>
    199. <el-icon class="fz" v-if="oneiSub === 10">
    200. <CoffeeCup :color="colors[10]" />
    201. </el-icon>
    202. </div>
    203. </div>
    204. </el-col>
    205. </el-row>
    206. </template>
    207. </div>
    208. <div class="sheep-footer flex-center">
    209. <div v-for="(ii, k) in footerList" :key="'ii' + k" class="sheep-footer-items">
    210. <el-icon class="fz" v-if="ii === 0">
    211. <StarFilled :color="colors[0]" />
    212. </el-icon>
    213. <el-icon class="fz" v-if="ii === 1">
    214. <Aim :color="colors[1]" />
    215. </el-icon>
    216. <el-icon class="fz" v-if="ii === 2">
    217. <Grid :color="colors[2]" />
    218. </el-icon>
    219. <el-icon class="fz" v-if="ii === 3">
    220. <HelpFilled :color="colors[3]" />
    221. </el-icon>
    222. <el-icon class="fz" v-if="ii === 4">
    223. <Star :color="colors[4]" />
    224. </el-icon>
    225. <el-icon class="fz" v-if="ii === 5">
    226. <Menu :color="colors[5]" />
    227. </el-icon>
    228. <el-icon class="fz" v-if="ii === 6">
    229. <Camera :color="colors[6]" />
    230. </el-icon>
    231. <el-icon class="fz" v-if="ii === 7">
    232. <Bicycle :color="colors[7]" />
    233. </el-icon>
    234. <el-icon class="fz" v-if="ii === 8">
    235. <IceTea :color="colors[8]" />
    236. </el-icon>
    237. <el-icon class="fz" v-if="ii === 9">
    238. <ColdDrink :color="colors[9]" />
    239. </el-icon>
    240. <el-icon class="fz" v-if="ii === 10">
    241. <CoffeeCup :color="colors[10]" />
    242. </el-icon>
    243. <div class="boom-class" v-if="ii === 'boom'">💥</div>
    244. </div>
    245. </div>
    246. </div>
    247. </template>
    248. <style scoped lang="less">
    249. .flex-center {
    250. display: flex;
    251. align-items: center;
    252. }
    253. .el-row {
    254. // margin-top: 3rem;
    255. height: 28%;
    256. }
    257. .fz {
    258. font-size: 3rem;
    259. border: 1px solid #dfe5f9;
    260. // box-shadow: 2px 2px 10px #f3f6fe;
    261. background: #f3f6fe;
    262. border-radius: 5px;
    263. }
    264. .pic-list {
    265. position: relative;
    266. width: 100%;
    267. height: 100%;
    268. &-item {
    269. position: absolute;
    270. left: 10vw;
    271. cursor: pointer;
    272. transition: all 0.3s;
    273. &:nth-child(1n) {
    274. top: calc(var(--i) * 1.5rem);
    275. }
    276. &.true {
    277. box-shadow: 0 -55px 0 0 #dfe5f9 inset;
    278. }
    279. // &:nth-child(even) {
    280. // top: 2rem;
    281. // }
    282. }
    283. }
    284. .sheep-main {
    285. flex: 1;
    286. &-wrap {
    287. height: calc(100% - 80px);
    288. }
    289. }
    290. .sheep-footer {
    291. height: 80px;
    292. width: 100%;
    293. // border: 2px solid #298df9;
    294. border: 2px solid #778899;
    295. background: #010206;
    296. .sheep-footer-items {
    297. height: 80px;
    298. width: calc(100% / 7);
    299. margin-left: 8px;
    300. display: flex;
    301. align-items: center;
    302. justify-content: center;
    303. .boom-class {
    304. font-size: 3rem;
    305. animation: myMove 3s ease-in-out infinite;
    306. }
    307. @keyframes myMove {
    308. 0% {
    309. opacity: 1;
    310. }
    311. 100% {
    312. opacity: 0;
    313. }
    314. }
    315. // border-right: 1px solid #dfe5f9;
    316. }
    317. }
    318. </style>

    3.核心逻辑分步骤详解

    import { ref, type Ref } from "vue";

    import { ElMessage, ElMessageBox } from "element-plus";

    import { useSheepStore } from "@/stores/sheep";

    // 关卡数据

    import data from "./data.json";

    // 颜色

    import constants from "./constants";

    // pinia 控制关卡

    const store = useSheepStore();

    首先引入data.json数据是渲染中间的页面内容,即是:

    中间的就叫卡片区域吧,卡片分为半个遮挡和整个遮挡,在data数据里面配置:

    "full": true

     默认是半个遮挡,配置了"full": true就表示这块的卡片是全遮挡的效果:

    :style="!onei.full ? `--i:${onekSub}` : `--i:0`"

    :class="onei.full && onei.oneSub.length > 1 ? 'true' : ''" :key="'i' + onekSub"

    css: 使用了var的变量形式,来控制是否需要top下移,&.true来控制是否有下一级的卡片的样式

    &:nth-child(1n) {

    top: calc(var(--i) * 1.5rem);

    }

    &.true {

            box-shadow: 0 -55px 0 0 #dfe5f9 inset;

    }

    data.json里面的数据oneSub的选值范围是:0-10

    这和dom渲染层的息息相关:卡片使用的是简单的icon也可以是其他类型的元素,你觉得好看即可。

    1. class="fz" v-if="oneiSub === 0">
    2. <StarFilled :color="colors[0]" />
    3. <el-icon class="fz" v-if="oneiSub === 1">
    4. <Aim :color="colors[1]" />
    5. el-icon>
    6. <el-icon class="fz" v-if="oneiSub === 2">
    7. <Grid :color="colors[2]" />
    8. el-icon>
    9. <el-icon class="fz" v-if="oneiSub === 3">
    10. <HelpFilled :color="colors[3]" />
    11. el-icon>
    12. <el-icon class="fz" v-if="oneiSub === 4">
    13. <Star :color="colors[4]" />
    14. el-icon>
    15. <el-icon class="fz" v-if="oneiSub === 5">
    16. <Menu :color="colors[5]" />
    17. el-icon>
    18. <el-icon class="fz" v-if="oneiSub === 6">
    19. <Camera :color="colors[6]" />
    20. el-icon>
    21. <el-icon class="fz" v-if="oneiSub === 7">
    22. <Bicycle :color="colors[7]" />
    23. el-icon>
    24. <el-icon class="fz" v-if="oneiSub === 8">
    25. <IceTea :color="colors[8]" />
    26. el-icon>
    27. <el-icon class="fz" v-if="oneiSub === 9">
    28. <ColdDrink :color="colors[9]" />
    29. el-icon>
    30. <el-icon class="fz" v-if="oneiSub === 10">
    31. <CoffeeCup :color="colors[10]" />
    32. el-icon>

    这里只提供11中卡片的效果,可以扩展添加,需要修改代码。

    接下来是:

    // 七个槽位

    // const footerList = ref([0, 1, 2, 3, 4, 5, 6]);

    const footerList: Ref | [any]> = ref([]);

    const colors = ref(constants.colors);

    // 关卡响应式

    const totalList: Ref | [any]> = ref([]);

    totalList.value = data["list1"]; // 默认第一关

    // 控制动画效果结束才能点击

    const isNotClick = ref(false);

    7个槽位在底部需要变化展示,做成响应式。totalList是动态变化的卡片数据集。totalList.value = data["list1"] ,默认第一关。爆炸💥的电话效果有延迟,需要控制在结束之后才能进行卡片的点击。

    然后就是核心的卡片点击事件,需要做哪些逻辑控制呢?先看源代码,已经提前做了备注:

    1. // 点击控制事件
    2. function handleClick(
    3. i: number,
    4. k: number,
    5. onei: { oneSub: string | Array },
    6. onek: number,
    7. oneiSub: Array,
    8. onekSub: number
    9. ) {
    10. console.log(i, k, onei, onek, oneiSub, onekSub, "测试");
    11. if (isNotClick.value) {
    12. return false;
    13. }
    14. // 内层不能点击
    15. if (onekSub !== onei.oneSub.length - 1) {
    16. return false;
    17. }
    18. // 前置点击如果槽位满了还没有消除完
    19. fullFun()
    20. // 关卡的消除
    21. let tempList = fixFun(k, onekSub, onek, oneiSub)
    22. // 消除动作 和 添加爆炸效果
    23. if (footerList.value.length > 2) {
    24. isNotClick.value = true
    25. const { list, flag } = eliminationFunction(footerList.value)
    26. footerList.value = list;
    27. if (flag) {
    28. footerList.value = addBoomFunction(footerList.value);
    29. }
    30. setTimeout(() => {
    31. const { list, flag } = eliminationFunction(footerList.value)
    32. footerList.value = list;
    33. isNotClick.value = false
    34. }, 1000);
    35. // 进入下一关
    36. nextFun(tempList)
    37. }
    38. // 挑战失败
    39. failFun(tempList)
    40. console.log(footerList, tempList, "tempList");
    41. }

    首先是函数的签名,接受最上层级的i对象,k索引,然后是中层的onei对象,onek索引,最后是父级的oneiSub对象,onekSub索引。判断条件需要前置,判断能否点击isNotClick,内层不能点击

    if (isNotClick.value) {

            return false;

    }

    // 前置点击如果槽位满了还没有消除完

    fullFun()函数判断如果槽位满了还没有消除完,就是挑战失败

    1. function fullFun() {
    2. if (footerList.value.length === 7) {
    3. ElMessage.closeAll();
    4. ElMessageBox.alert("挑战失败,点击确定返回!", "Warning", {
    5. confirmButtonText: "确定",
    6. type: "warning",
    7. showClose: false,
    8. }).then(() => {
    9. location.reload();
    10. });
    11. return false;
    12. }
    13. }
    '
    运行

    如何添加爆炸💥效果:

    思路是在三个相同消除之后添加,添加在totalList数据之中 ,效果展示完成之后立即进行totalList数据重置操作。

    1. // 关卡的消除
    2. let tempList = fixFun(k, onekSub, onek, oneiSub)
    3. // 消除动作 和 添加爆炸效果
    4. if (footerList.value.length > 2) {
    5. isNotClick.value = true
    6. const { list, flag } = eliminationFunction(footerList.value)
    7. footerList.value = list;
    8. if (flag) {
    9. footerList.value = addBoomFunction(footerList.value);
    10. }
    11. setTimeout(() => {
    12. const { list, flag } = eliminationFunction(footerList.value)
    13. footerList.value = list;
    14. isNotClick.value = false
    15. }, 1000);
    16. // 进入下一关
    17. nextFun(tempList)
    18. }

    css 添加的方法:

    1. .boom-class {
    2. font-size: 3rem;
    3. animation: myMove 3s ease-in-out infinite;
    4. }
    5. @keyframes myMove {
    6. 0% {
    7. opacity: 1;
    8. }
    9. 100% {
    10. opacity: 0;
    11. }
    12. }

    消除函数eliminationFunction逻辑的控制,flag用来进行是否成功消除:

    1. // 消除函数
    2. function eliminationFunction(list: any[]) {
    3. let flag: boolean = false;
    4. for (let k = 0; k < list.length - 2; k++) {
    5. const temp = list;
    6. const arr = temp.slice(k, k + 3);
    7. console.log(k, arr);
    8. if (arr[0] === arr[1] && arr[1] === arr[2] && arr[0] === arr[2]) {
    9. list.splice(k + 2);
    10. list.splice(k + 1);
    11. list.splice(k, 1);
    12. flag = true
    13. break;
    14. }
    15. }
    16. return { list, flag };
    17. }

    添加addBoomFunction爆炸函数:

    1. // 实现爆炸💥效果
    2. function addBoomFunction(list: any[]) {
    3. const temp = JSON.parse(JSON.stringify([...list, ...['boom', 'boom', 'boom']]))
    4. return temp;
    5. }

    挑战失败如何判断呢?

    1. //fail
    2. function failFun(tempList: any[]) {
    3. setTimeout(() => {
    4. if (footerList.value.length > 0 && !jugeList(tempList)) {
    5. ElMessage.closeAll();
    6. ElMessageBox.alert("挑战失败,点击确定返回!", "Warning", {
    7. confirmButtonText: "确定",
    8. type: "warning",
    9. showClose: false,
    10. }).then(() => {
    11. location.reload();
    12. });
    13. return false;
    14. }
    15. }, 1002)
    16. }

    jugeList函数是对目前存在的卡片集合进行长度判断,如何卡片不存在,但是槽位的数据不为空的情况下,说明没有消除完,就判断要重新开始挑战: 

    1. // 判断是否过关
    2. function jugeList(list: any[]) {
    3. let temp: any = [];
    4. list?.forEach((oeni: { one: any }) => {
    5. oeni?.one?.forEach((sub: { oneSub: any }) => {
    6. temp = [...temp, ...sub.oneSub];
    7. });
    8. });
    9. return temp.length;
    10. }

    最后是挑战成功就可以进行下一关:

    1. // next
    2. function nextFun(tempList: any[]) {
    3. setTimeout(() => {
    4. if (!footerList.value.length && !jugeList(tempList)) {
    5. // debugger
    6. ElMessage.closeAll();
    7. ElMessage.success("恭喜您,挑战成功!进入下一关");
    8. store.step++;
    9. const inStep: string = "list" + (store.step + 1);
    10. totalList.value = JSON.parse(JSON.stringify(data))[inStep];
    11. footerList.value = [];
    12. }
    13. }, 1001)
    14. }

    如何卡片不存在,但是槽位的数据为空的情况下,说明消除完了,就可以进入下一关进行挑战,难度也将升级!

    4.总结

    最近是由于玩了羊了个羊的小程序,有所感悟,思考了这个游戏的整体的玩法,如何去操作,然后想到了可以实现一个前端网页版本的羊了个羊,这里面有一些自己的设计思考是很重要的,花了一个星期左右来实现,中间遇到了如何消除,如何控制挑战失败,成功的问题,并且一一解决了,可以想到如果前端来做这个游戏怎么在最优的方案上,书写可以扩展的dom,来适配很多不同的关卡的元素或者是我们需要什么样的数据结构,方便后续的关卡的升级。这里解决的方案是配合json,数据是数组嵌套类型,元素是需要循环来调用的,什么类型的卡片是需要提前有个范围的,这样是可扩展的。最后的操作,或者撤销,恢复等操作(这里没有实现)本质上也是对于数据的操作。终而言之:数据驱动页面,才是我们追求的。最后,各位同学一起多思考一下背后的实现,让我们用技术来创作更多有趣的事情吧~❤️
     

    个人主页:KinHKin(五年前端)的博客_CSDN博客-vue,css,中秋活动领域博主

    在线演示:KinHKin

    fllow我的github:   rondsjinhuajin (承吾) · GitHub

  • 相关阅读:
    继NOIP2022结束以后(文化课复建)
    【Unity Shader】GLSL
    MFC Windows 程序设计[114]之多样下拉列表框
    初识 Python
    特征值和特征向量及其在机器学习中的应用
    Redis(八) - Redis企业实战之优惠券秒杀
    谷歌员工年薪中位数是苹果的4倍多,亚马逊CEO薪酬抵6千名员工 | 科技巨头薪资排名出炉
    D - Snuke Panic (1D)
    Java审计框架基础
    DBCO-PEG-Lentinan;Lentinan-PEG-DBCO;二苯并环辛炔聚乙二醇修饰香菇多糖
  • 原文地址:https://blog.csdn.net/weixin_42974827/article/details/127052101