基于JavaScript如何编写一个翻卡游戏

其他教程   发布日期:2023年06月20日   浏览次数:444

这篇文章主要介绍“基于JavaScript如何编写一个翻卡游戏”,在日常操作中,相信很多人在基于JavaScript如何编写一个翻卡游戏问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”基于JavaScript如何编写一个翻卡游戏”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

翻卡动画

假如我们的盒子模型不是个二维的平面,而是有个三维的体积,让它可以有正反两面,那我们在做的时候是不是只要将它真实的翻个面就可以了。让我们来想想将它变成三维的方法。 之后发现了这个属性:

  1. transform: translateZ(1px);

使用了它,就可以把盒子内部的元素与盒子的底部撑出个高度。

  1. <!-- html -->
  2. <div class="card">
  3. <div class="top">我是正面哦~</div>
  4. </div>

只用给叫做“top”的子盒子一个“距离父亲的距离”,再将叫做“card”的父盒子预先翻转180度

  1. rotateY(180deg)
,等到点击的时候给它翻回来
  1. transform: rotateY(0)
就可以了。
  1. .card{
  2. ...
  3. height: 100%;
  4. width: 100%;
  5. position: relative;
  6. transform-style: preserve-3d;
  7. transform: rotateY(180deg);
  8. transition: all 600ms;
  9. background: pink;
  10. &.select {
  11. transform: rotateY(0);
  12. }
  13. .top{
  14. ...
  15. height: 100%;
  16. width: 100%;
  17. position: absolute;
  18. top: 0;
  19. left: 0;
  20. box-sizing: border-box;
  21. background: white;
  22. border: 2px solid #b6a6dc;
  23. transform: translateZ(1px);
  24. }
  25. }

生成随机分布数组

我们先来说下在理想环境中,每个元素都能匀均出现(次数相等)的情况。再来说下不能均匀出现的情况下,怎样最大限度的均匀。

均匀元素下的随机算法

此算法脑内模型由西塔(&theta;)先生友情提供

假设我们一共需要20个元素,有5个不同类型的格子,正好每个格子出现4次。我们就有了一个待分配元素的集合W:

  1. const total = 20
  2. const icons = ['a', 'b', 'c', 'd', 'e']
  3. // => 得到集合W
  4. const W = ['a', 'a', 'a', 'a',
  5. 'b', 'b', 'b', 'b',
  6. 'c', 'c', 'c', 'c',
  7. 'd', 'd', 'd', 'd',
  8. 'e', 'e', 'e', 'e']

混淆集合

有个指针p从下标0开始,在长度为20的数组格子里面负责填图案,填图案的规律是从集合w中随机取一个元素,取完后删除该元素,p移动到下一个格子里,迭代至完成。

  1. function createRandomList(W: string[], total: number) {
  2. const list: any[] = []
  3. function it(time: number): any {
  4. if (time === 0) return list
  5. // 随机每次集合元素下标
  6. const randomNum = Math.floor(Math.random() * (W.length))
  7. list.push(W[randomNum]) // 新数组中加入随机到的元素
  8. W.splice(randomNum, 1) // 删除集合中的元素
  9. return it(--time)
  10. }
  11. return it(total)
  12. }

我们再让这个方法灵活一点,使它的返回结果能够随便指定格式:

  1. // fn非必传项,默认返回原数据
  2. function createRandomList(W: string[], total: number, fn: (<T>(icon: string, index?: number) => T) = icon => icon) {
  3. const list: any[] = []
  4. // 迭代器
  5. function it(time: number): any {
  6. if (time === 0) return list
  7. // 随机每次集合元素下标
  8. const randomNum = Math.floor(Math.random() * (W.length))
  9. list.push(fn(W[randomNum], total-time)) // 将元素和下标传入fn中,将fn的计算结果加入到新数组中
  10. W.splice(randomNum, 1) // 删除集合中的元素
  11. return it(--time)
  12. }
  13. return it(total)
  14. }

不均匀元素下的随机算法

  1. const W = []

不均匀元素,其实就是集合W里的元素分布规则改变了,混淆算法仍然不受影响。之后,让我们来思考下怎么定义一个“不均匀中的最大程度均匀”的集合。 将集合W分为两个部分: 最大可均匀分布部分 + 随机部分

最大可均匀分布的部分,它代表着icons中的每个元素都能出现相同的最多偶数次。可以这样得到它:

  • icons个数x2,得到完整一次配对需要多少格子

  • 总格子数 / 一次完整格子数,得到可以完整配对的最大次数n

  • 循环n次,每次循环往W里添加icons x 2

  1. // 得到最大重复次数
  2. const times = Math.floor(total / (icons.length * 2))
  3. for (let index = 0; index < times; index++)
  4. W.push(...icons, ...icons)

剩下的是需要随机分布的部分,它代表着,某几个元素可以在这里出现2次,剩下的则不会出现。

  • 总格子数 % icons个数x2, 得到剩下未分配的格子

  • 未分配格子 / 2, 就是需要随机从icons中取出的元素个数n,这个n一定小于icons的个数

  • 从icons中随机取n个数,可以采用每取一个数,将该数从原集合删除,重复n次的方法

  • 将得到的n个数x2,往W里添加

第(3)条是不是听起来很耳熟,好像前面做过,没错就是前面写的

  1. createRandomList
函数,W集合变成了icons,total变成了需要的个数n。
  1. // 剩下未分配的格子个数
  2. const lastCount = total % (icons.length * 2)
  3. // 从icons中随机获取n个数
  4. const lastList = createRandomList(icons, lastCount / 2)
  5. W.push(...lastList, ...lastList)

合在一起就是就是创建W的方法:

  1. function createW(icons: string[], total: number) {
  2. const times = Math.floor(total / (icons.length * 2))
  3. const lastCount = total % (icons.length * 2)
  4. const W = []
  5. for (let index = 0; index < times; index++)
  6. W.push(...icons, ...icons)
  7. const lastList = createRandomList(icons, lastCount / 2)
  8. W.push(...lastList, ...lastList)
  9. return W
  10. }

生成最终数组

完整的生成随机数组代码:

  1. function createW(icons: string[], total: number) {
  2. const times = Math.floor(total / (icons.length * 2))
  3. const lastCount = total % (icons.length * 2)
  4. const W = []
  5. for (let index = 0; index < times; index++)
  6. W.push(...icons, ...icons)
  7. const lastList = createRandomList(icons, lastCount / 2)
  8. W.push(...lastList, ...lastList)
  9. return W
  10. }
  11. function createRandomList(W: string[], total: number, fn: (<T>(icon: string, index?: number) => T) = icon => icon) {
  12. const list: any[] = []
  13. function it(time: number): any {
  14. if (time === 0) return list
  15. const randomNum = Math.floor(Math.random() * (W.length))
  16. list.push(fn(W[randomNum], total-time))
  17. W.splice(randomNum, 1)
  18. return it(--time)
  19. }
  20. return it(total)
  21. }
  22. // ['a', 'b', 'c', "d"] => ['c', 'd'...x15...'b', 'c', 'a']
  23. createRandomList(createW(icons, total), total)

点击事件

乱序的随机数组有了,点一点还不简单吗! 先让生成的数组属性更丰富一些,来帮助我们展示内容。

  1. type CardItem = { icon: string; isDel: boolean; isSelect: boolean, index: number }
  2. let list: CardItem[] = []
  3. // isSelect属性判断是否翻转,isDel属性判断是否已经消除,icon属性标注元素属性,index用来快速找到点击元素位于数组中的位置
  4. list = createRandomList(createW(icons, total), total, (icon: string, index) => ({ icon, isDel: false, isSelect: false, index }))

这下可以用生成的数组去展示了。接下来我们写个点击事件,接收参数是点击的数组元素:

  1. // isLock用来锁定动画完成前不能进行别的操作
  2. function handlerTap(card: CardItem) {
  3. if (isLock) return
  4. list[card.index].isSelect = true
  5. const selectors = list.filter(item => item.isSelect && !item.isDel)
  6. // 假如选择元素<2,直接返回,不走之后流程
  7. if (selectors.length <= 1) return
  8. isLock = true
  9. const [item1, item2] = selectors
  10. // 翻转动画完成后进行操作
  11. setTimeout(() => {
  12. // 如果选择的元素相同,则消除属性等于true
  13. if (item1.icon === item2.icon) {
  14. list[item1.index].isDel = true
  15. list[item2.index].isDel = true
  16. }
  17. //将所有卡牌翻转过背面
  18. list = list.map(item => ({...item, isSelect: false}))
  19. isLock = false
  20. // 判断是否所有卡牌都已经翻转完成
  21. if (list.every(item => item.isDel)) console.log( "your win!")
  22. }, 800)
  23. }

完整代码

100行整)。

  1. <script lang="ts">
  2. type CardItem = { icon: string; isDel: boolean; isSelect: boolean, index: number }
  3. const icons = ['a', 'b', 'c', "d"]
  4. const total = 20
  5. let list: CardItem[] = []
  6. let isLock = false
  7. function handlerTap(card: CardItem) {
  8. if (isLock) return
  9. list[card.index].isSelect = true
  10. const selectors = list.filter(item => item.isSelect && !item.isDel)
  11. if (selectors.length <= 1) return
  12. isLock = true
  13. const [item1, item2] = selectors
  14. setTimeout(() => {
  15. if (item1.icon === item2.icon) {
  16. list[item1.index].isDel = true
  17. list[item2.index].isDel = true
  18. }
  19. list = list.map(item => ({...item, isSelect: false}))
  20. isLock = false
  21. if (list.every(item => item.isDel)) console.log( "your win!")
  22. }, 800)
  23. }
  24. function createW(icons: string[], total: number) {
  25. const times = Math.floor(total / (icons.length * 2))
  26. const lastCount = total % (icons.length * 2)
  27. const W = []
  28. for (let index = 0; index < times; index++)
  29. W.push(...icons, ...icons)
  30. const lastList = createRandomList(icons, lastCount / 2)
  31. W.push(...lastList, ...lastList)
  32. return W
  33. }
  34. function createRandomList(W: string[], total: number, fn: (<T>(icon: string, index?: number) => T) = icon => icon) {
  35. const list: any[] = []
  36. function it(time: number): any {
  37. if (time === 0) return list
  38. const randomNum = Math.floor(Math.random() * (W.length))
  39. list.push(fn(W[randomNum], total-time))
  40. W.splice(randomNum, 1)
  41. return it(--time)
  42. }
  43. return it(total)
  44. }
  45. list = createRandomList(createW(icons, total),
  46. total,
  47. (icon: string, index) => ({ icon, isDel: false, isSelect: false, index }))
  48. </script>
  49. <div class="game-box">
  50. {#each list as item}
  51. <div class="grid">
  52. {#if !item.isDel}
  53. <div class="card {item.isSelect && 'select'}" on:click="{() => handlerTap(item)}">
  54. <div class="top">{item.icon}</div>
  55. </div>
  56. {/if}
  57. </div>
  58. {/each}
  59. </div>
  60. <style lang="less">
  61. .game-box{
  62. margin: 10px auto 0;
  63. width: 90vw;
  64. height: 80vh;
  65. display: grid;
  66. grid-template-columns: repeat(4, calc(100% / 4 - 3px));
  67. grid-template-rows: repeat(5, calc(100% / 5 - 3px));
  68. grid-row-gap:3px;
  69. grid-column-gap: 3px;
  70. .card{
  71. height: 100%;
  72. width: 100%;
  73. box-sizing: border-box;
  74. position: relative;
  75. transform-style: preserve-3d;
  76. transform: rotateY(180deg);
  77. transition: all 600ms;
  78. background: pink;
  79. &.select {
  80. transform: rotateY(0);
  81. }
  82. .top{
  83. height: 100%;
  84. width: 100%;
  85. position: absolute;
  86. top: 0;
  87. left: 0;
  88. box-sizing: border-box;
  89. display: flex;
  90. justify-content: center;
  91. align-items: center;
  92. background: white;
  93. border: 2px solid #b6a6dc;
  94. transform: translateZ(1px);
  95. }
  96. }
  97. }
  98. </style>

以上就是基于JavaScript如何编写一个翻卡游戏的详细内容,更多关于基于JavaScript如何编写一个翻卡游戏的资料请关注九品源码其它相关文章!