最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Vue3制作簡單的消消樂游戲

 更新時(shí)間:2022年05月27日 08:16:29   作者:沐華  
這篇文章主要為大家介紹了如何利用Vue3制作簡單的消消樂游戲,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起動(dòng)手試一試

游戲介紹

先看一下

好吧,我知道界面有點(diǎn)丑 →_→

核心思路

游戲步驟主要就是:消除、下落、補(bǔ)充、移動(dòng),采用三種狀態(tài)來區(qū)分需要?jiǎng)h除的(remove)、新添加的(add)、和正常的方塊(normal)

  • 主要就是生成小方塊列表后,馬上保存每一個(gè)方塊上下左右方塊的信息
  • 然后判斷每一個(gè)方塊和上下或和左右類型相同即為需要消除,并把該方塊狀態(tài)改為 remove
  • 然后通過定位改變 top 來控制下落,同時(shí)要把消除的位置上移,這樣補(bǔ)充的時(shí)候才能在對應(yīng)空位上顯示,這里專門用了一個(gè)矩陣來保存所有對應(yīng)格子信息,區(qū)分出哪些格子是需要消除/補(bǔ)充的
  • 移動(dòng)就比較簡單了,由于每個(gè)方塊上都保存了自己的上下左右信息,所以只需要交換就行了

有一個(gè)坑,就是 key,由于 diff 算法的原因,不需要重新渲染就要保證key是唯一的,比如下落的也重新渲染視覺效果會(huì)很奇怪

核心代碼

html

以下是矩陣區(qū)域所有html,就是用一個(gè)div來做的,根據(jù)類型給不同類名,然后雪糕全是背景圖片

<div class="stage">
  <div
    v-for="item in data"
    :style="{
      left: `${item.positionLeft}px`,
      top: `${item.positionTop}px`,
    }"
    :key="item.key"
    :class="[
      'square',
      `type${item.type}`,
      `scale${item.scale}`,
      { active: item.active },
    ]"
    @click="handleClick(item)"
  ></div>
</div>

js

js 部分主要是封裝了一個(gè)類,方便統(tǒng)一管理操作

export default class Stage implements IXXL {
  x: number // x和y 是游戲舞臺(tái)行列方塊個(gè)數(shù)
  y: number
  size: number // 方塊大小
  typeCount = 7 // 方塊類型個(gè)數(shù)
  matrix: Array<any> = [] // 方塊矩陣,用于每次消除之后根據(jù)矩陣規(guī)則生成新的游戲棋盤
  data: Array<any> = [] // 用于渲染頁面
  isHandle = false // 游戲是否正在消除/下落/添加處理中
  isSelect = false // 是否有選擇
  score = 0 // 分?jǐn)?shù)
  target1: any = { active: false } // 選中的方塊
  target2: any = {}
  constructor(x: number, y: number, size: number) {
    this.x = x
    this.y = y
    this.size = size
    this.getMatrix() // 生成矩陣
    this.init(true) // 生成 data 渲染用
  }
  getMatrix(){}
  init(){}
  // 循環(huán)執(zhí)行
  gameLoop(){}
  // 點(diǎn)擊
  click(){}
  // 換位
  swap(){}
  // 刪除
  remove(){}
  // 下落
  down(){}
  // 補(bǔ)充
  add(){}
}

游戲開始/循環(huán)

// 要等動(dòng)畫執(zhí)行完,所以用 await
async gameLoop(bool: boolean = false) {
    // 結(jié)束游戲后重新開始時(shí)分?jǐn)?shù)清0
    if (bool) this.score = 0
    // 游戲狀態(tài)改為正在執(zhí)行中,控制在動(dòng)畫執(zhí)行過程中不能點(diǎn)擊交換
    this.isHandle = true
    // 找出需要?jiǎng)h除的
    await this.remove()
    // 用于檢測點(diǎn)擊交換后判斷有沒有需要?jiǎng)h除的,沒有就再換回來
    let status = this.data.some((item) => item.status === "remove")
    // 只要有刪除了的,執(zhí)行上面的下落、補(bǔ)充,補(bǔ)充后再循環(huán)找有沒有可以刪除的
    while (this.data.some((item) => item.status === "remove")) {
      await this.down()
      await this.add()
      await this.remove()
    }
    // 所有能刪除的刪除后,更改狀態(tài),然后就可以點(diǎn)擊了
    this.isHandle = false
    return status
}

刪除

注意 狀態(tài)為 remove 的實(shí)際沒有刪除,只是頁面上看不到了,到補(bǔ)充的時(shí)候才會(huì)刪除掉狀態(tài)為 remove

// 清除
remove() {
    return new Promise((resolve, reject) => {
      const { data } = this
      data.forEach((item) => {
        const { left, right, top, bottom, type } = item
        // 如果自己 + 自己的左和右 類型都一樣,狀態(tài)變更為刪除
        if (left?.type == type && right?.type == type) {
          left.status = "remove"
          item.status = "remove"
          right.status = "remove"
        }
        // 如果自己 + 自己的上和下 類型都一樣,狀態(tài)變更為刪除
        if (top?.type == type && bottom?.type == type) {
          top.status = "remove"
          item.status = "remove"
          bottom.status = "remove"
        }
      })
      setTimeout(() => {
        // 執(zhí)行刪除動(dòng)畫,頁面上看不到了,并統(tǒng)計(jì)分?jǐn)?shù),實(shí)際這時(shí)還沒刪除
        data.forEach((item, index) => {
          if (item.status === "remove") {
            item.scale = 0
            this.score += 1
          }
        })
        // 這里延遲100毫秒是首次進(jìn)頁面的時(shí)候,先看到格子有東西,不然會(huì)是空的
      }, 100)
      // 動(dòng)畫時(shí)長500毫秒 css 那邊定義了,所以延遲500毫秒
      setTimeout(() => {
        resolve(true)
      }, 500)
    })
}

下落

這里有個(gè)坑。除了要把刪除格子上面的下落下來之外,還需要把已經(jīng)刪除(狀態(tài)為刪除,頁面上看不到了的)的格子上位到,上面的空位上,否則,新增的格子會(huì)從下面冒出來

// 下落
down() {
    return new Promise((resolve, reject) => {
      const { data, size, x, y } = this
      data.forEach((item, index) => {
        let distance = 0 // 移動(dòng)格數(shù)
        if (item.status === "remove") {
          // 刪除的位置上移,調(diào)整新增格子的位置
          let top = item.top
          // 統(tǒng)計(jì)需要上移多少步
          while (top) {
            if (top.status !== "remove") {
              distance += 1
            }
            top = top.top
          }
          // 上移
          if (distance) {
            item.y -= distance
            item.positionTop = item.positionTop - size * distance
          }
        } else {
          let bottom = item.bottom
          // 統(tǒng)計(jì)需要下落多少步
          while (bottom) {
            if (bottom.status === "remove") {
              distance += 1
            }
            bottom = bottom.bottom
          }
          // 下落
          if (distance) {
            item.y += distance
            item.positionTop = item.positionTop + size * distance
          }
        }
      })
      setTimeout(() => {
        resolve(true)
      }, 500)
    })
}

添加

可以想象到,在下落執(zhí)行完之后,頁面中的矩陣,是所有格子都有的,只是看起來空的格子,實(shí)際上是刪除格子在那占位,然后只要根據(jù)順序重新生成矩陣,并保留每個(gè)非remove格子的狀態(tài),是remove的就重新生成,達(dá)到替換補(bǔ)充的效果

// 添加
add() {
    return new Promise((resolve, reject) => {
      const { size, matrix } = this
      // 重置矩陣為空
      this.getMatrix()
      // 把當(dāng)前所有格子信息保存為矩陣
      this.matrix = matrix.map((row, rowIndex) =>
        row.map((col: any, colIndex: number) => {
          return this.data.find((item) => {
            return colIndex == item.x && rowIndex == item.y
          })
        })
      )
      // 根據(jù)矩陣需要清除的位置替換新方塊
      this.init()
      setTimeout(() => {
        // 新增的格子執(zhí)行動(dòng)畫
        this.data.forEach((item) => {
          if (item.status === "add") {
            item.scale = 1
            item.status = "normal"
          }
        })
      }, 100)
      // 動(dòng)畫結(jié)束
      setTimeout(() => {
        resolve(true)
      }, 500)
    })
}

接下來后面的邏輯都比較簡單了,沒啥說的,都寫在注釋里了

生成矩陣/數(shù)據(jù)

// 生成全部為空的矩陣
getMatrix() {
    const { x, y } = this
    const row = new Array(x).fill(undefined)
    const matrix = new Array(y).fill(undefined).map((item) => row)
    this.matrix = matrix
}
// 生成小方塊
init(bool: boolean = false) {
    const { x, y, typeCount, matrix, size } = this
    const data: Array<any> = []
    // 這里用兩個(gè)指針,沒有用嵌套循環(huán),減少復(fù)雜度
    let _x = 0
    let _y = 0
    for (let i = 0, len = Math.pow(x, 2); i < len; i++) {
      let item
      try {
        item = matrix[_y][_x]
      } catch (e) {}
      // 根據(jù)矩陣信息來生成方塊
      let flag: boolean = item && item.status !== "remove"
      // 每一個(gè)方塊的信息
      let obj = {
        type: flag ? item.type : Math.floor(Math.random() * typeCount),
        x: _x,
        y: _y,
        status: bool ? "normal" : flag ? "normal" : "add",
        positionLeft: flag ? item.positionLeft : size * _x,
        positionTop: flag ? item.positionTop : size * _y,
        left: undefined,
        top: undefined,
        bottom: undefined,
        right: undefined,
        scale: bool ? 1 : flag ? 1 : 0,
        key: item ? item.key + i : `${_x}${_y}`,
        active: false,
      }
      data.push(obj)
      _x++
      if (_x == x) {
        _x = 0
        _y++
      }
    }
    // 保存每個(gè)格子上下左右的格子信息
    data.forEach((square) => {
      square.left = data.find(
        (item) => item.x == square.x - 1 && item.y == square.y
      )
      square.right = data.find(
        (item) => item.x == square.x + 1 && item.y == square.y
      )
      square.top = data.find(
        (item) => item.x == square.x && item.y == square.y - 1
      )
      square.bottom = data.find(
        (item) => item.x == square.x && item.y == square.y + 1
      )
    })
    this.data = data
}

點(diǎn)擊

// 點(diǎn)擊小方塊
click(target: any) {
    // 游戲動(dòng)畫正在處理中的時(shí)候,不給點(diǎn)擊
    if (this.isHandle) return
    // console.log(target)
    const { isSelect } = this
    // 如果沒有選擇過的
    if (!isSelect) {
      // 選擇第一個(gè)
      target.active = true
      this.target1 = target
      this.isSelect = true
    } else {
      // 選擇第二個(gè)
      if (this.target1 === target) return
      this.target1.active = false
      // 如果是相鄰的
      if (
        ["left", "top", "bottom", "right"].some(
          (item) => this.target1[item] == target
        )
      ) {
        this.target2 = target
        ;(async () => {
          // 調(diào)換位置
          await this.swap()
          // 會(huì)返回一個(gè)有沒有可以刪除的,的狀態(tài)
          let res = await this.gameLoop()
          // 沒有就再次調(diào)換位置,還原
          if (!res) {
            await this.swap()
          }
        })()
        this.isSelect = false
      } else {
        // 如果不是相鄰的
        target.active = true
        this.target1 = target
        this.isSelect = true
      }
    }
}

換位置

這里的邏輯主要就是交換兩個(gè)方塊的位置信息,然后重新生成上下左右,就ok 了

// 換位置
swap() {
    return new Promise((resolve, reject) => {
      const { target1, target2, data } = this
      const { positionLeft: pl1, positionTop: pt1, x: x1, y: y1 } = target1
      const { positionLeft: pl2, positionTop: pt2, x: x2, y: y2 } = target2
      setTimeout(() => {
        target1.positionLeft = pl2
        target1.positionTop = pt2
        target1.x = x2
        target1.y = y2
        target2.positionLeft = pl1
        target2.positionTop = pt1
        target2.x = x1
        target2.y = y1
        data.forEach((square) => {
          square.left = data.find(
            (item) => item.x == square.x - 1 && item.y == square.y
          )
          square.right = data.find(
            (item) => item.x == square.x + 1 && item.y == square.y
          )
          square.top = data.find(
            (item) => item.x == square.x && item.y == square.y - 1
          )
          square.bottom = data.find(
            (item) => item.x == square.x && item.y == square.y + 1
          )
        })
      }, 0)
      setTimeout(() => {
        resolve(true)
      }, 500)
    })
}

到此這篇關(guān)于基于Vue3制作簡單的消消樂游戲的文章就介紹到這了,更多相關(guān)Vue3消消樂游戲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue 菜單欄點(diǎn)擊切換單個(gè)class(高亮)的方法

    Vue 菜單欄點(diǎn)擊切換單個(gè)class(高亮)的方法

    今天小編就為大家分享一篇Vue 菜單欄點(diǎn)擊切換單個(gè)class(高亮)的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • Vue3的效率提升主要表現(xiàn)在哪些方面示例解析

    Vue3的效率提升主要表現(xiàn)在哪些方面示例解析

    Vue3帶來了許多性能優(yōu)化和效率提升的特性,本文將重點(diǎn)討論Vue3在靜態(tài)提升、預(yù)字符串化、緩存事件處理函數(shù)、Block?Tree和PatchFlag方面的改進(jìn),我們將通過對比Vue2和Vue3的編譯結(jié)果來說明這些方面的效率提升
    2023-12-12
  • electron?vue?模仿qq登錄界面功能實(shí)現(xiàn)

    electron?vue?模仿qq登錄界面功能實(shí)現(xiàn)

    這篇文章主要介紹了electron?vue?模仿qq登錄界面,首先使用vuecli創(chuàng)建vue項(xiàng)目,安裝electron,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08
  • Vue頁面首次載入優(yōu)化的全過程

    Vue頁面首次載入優(yōu)化的全過程

    凡是做SPA的項(xiàng)目,特別是移動(dòng)端的SAP項(xiàng)目,首屏加載速度必定是一個(gè)繞不過去的話題,下面這篇文章主要給大家介紹了關(guān)于Vue頁面首次載入優(yōu)化的相關(guān)資料,需要的朋友可以參考下
    2021-12-12
  • 關(guān)于vue-router的那些事兒

    關(guān)于vue-router的那些事兒

    要學(xué)習(xí)vue-router就要先知道這里的路由是什么?為什么我們不能像原來一樣直接用標(biāo)簽編寫鏈接哪?vue-router如何使用?常見路由操作有哪些?等等這些問題,就是本篇要探討的主要問題,感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧
    2018-05-05
  • vue用復(fù)選框?qū)崿F(xiàn)組件且支持單選和多選操作方式

    vue用復(fù)選框?qū)崿F(xiàn)組件且支持單選和多選操作方式

    這篇文章主要介紹了vue用復(fù)選框?qū)崿F(xiàn)組件且支持單選和多選操作方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • vue 動(dòng)態(tài)組件component

    vue 動(dòng)態(tài)組件component

    這篇文章主要介紹了 vue 動(dòng)態(tài)組件component ,vue提供了一個(gè)內(nèi)置的<component>,專門用來實(shí)現(xiàn)動(dòng)態(tài)組件的渲染,這個(gè)標(biāo)簽就相當(dāng)于一個(gè)占位符,需要使用is屬性指定綁定的組件,想了解更多詳細(xì)內(nèi)容的小伙伴請參考下面文章的具體內(nèi)容
    2021-11-11
  • Vue3如何根據(jù)搜索框內(nèi)容跳轉(zhuǎn)至本頁面指定位置

    Vue3如何根據(jù)搜索框內(nèi)容跳轉(zhuǎn)至本頁面指定位置

    在開發(fā)中我們經(jīng)常遇到這樣的需求,根據(jù)要求跳轉(zhuǎn)至本頁面指定位置,這篇文章主要給大家介紹了關(guān)于Vue3如何根據(jù)搜索框內(nèi)容跳轉(zhuǎn)至本頁面指定位置的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • Vue監(jiān)聽屬性圖文實(shí)例詳解

    Vue監(jiān)聽屬性圖文實(shí)例詳解

    監(jiān)聽屬性可以針對某個(gè)屬性進(jìn)行監(jiān)聽,當(dāng)監(jiān)聽的屬性的值發(fā)生了變化,則會(huì)執(zhí)行相應(yīng)的函數(shù),下面這篇文章主要給大家介紹了關(guān)于Vue監(jiān)聽屬性的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • vue computed無法得到this的屬性或方法的解決

    vue computed無法得到this的屬性或方法的解決

    這篇文章主要介紹了vue computed無法得到this的屬性或方法的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07

最新評論

濮阳市| 靖江市| 涿鹿县| 临清市| 衢州市| 巴楚县| 秭归县| 南开区| 汉阴县| 天峨县| 大石桥市| 航空| 阜南县| 永清县| 绍兴市| 理塘县| 沙河市| 平邑县| 马龙县| 深泽县| 响水县| 宁乡县| 白河县| 临猗县| 五大连池市| 芜湖市| 扶沟县| 绥阳县| 青海省| 开封县| 田东县| 郁南县| 荥经县| 三台县| 岑溪市| 清丰县| 抚州市| 云浮市| 垦利县| 抚顺县| 启东市|