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

使用Vue3實(shí)現(xiàn)羊了個(gè)羊的算法

 更新時(shí)間:2022年09月21日 15:21:30   作者:老驥farmer  
這篇文章主要介紹了使用Vue3實(shí)現(xiàn)羊了個(gè)羊的算法,初始化的隨機(jī)位置算法,通過(guò)實(shí)例代碼介紹了計(jì)算偏移量的方法,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

前言

這兩天社區(qū)很多羊了個(gè)羊的web實(shí)現(xiàn),雖然各種實(shí)現(xiàn)花里花哨,然而,并沒(méi)有一個(gè)一個(gè)jy能給他說(shuō)清楚到底怎么實(shí)現(xiàn)的,由于可怕的求知欲,自己來(lái)吧!

大綱

羊了個(gè)羊這個(gè)現(xiàn)象級(jí)游戲之所以能成功,不是因?yàn)樗?code>原神一樣,靠著質(zhì)量、體驗(yàn)、劇情你愛(ài)不釋手

他靠的是,讓你愛(ài)不釋手,人家玩的是營(yíng)銷,玩的是人性,也許你壓根就過(guò)不了關(guān)!

他的技術(shù)實(shí)現(xiàn),其實(shí)相當(dāng)簡(jiǎn)單,在技術(shù)上從來(lái)沒(méi)有什么高深的東西,

果然,高深的技術(shù)總是顯得這么樸實(shí)無(wú)華!

最難的部分也就是算法了,我也大致的鉆研了一下,但是這個(gè)算法坦率的講不是我發(fā)明的, 我只是站在巨人的肩膀上

他的算法實(shí)現(xiàn)的難點(diǎn)我以為有四方面

  • 1、 初始化的隨機(jī)位置算法
  • 2、 檢查是否被覆算法
  • 3、 三連匹配算法
  • 4、隊(duì)列區(qū)排序算法

在線演示

https://code.juejin.cn/pen/7144922644788297735

初始化的隨機(jī)位置算法

在理解算法之前,我們先大致看元數(shù)據(jù)

他需要包含 一些必備的屬性, 默認(rèn)的覆蓋情況,是否被選中的狀態(tài),icon 圖標(biāo),icon 的唯一id x 坐標(biāo) y坐標(biāo)

const scene=({
      isCover: false, // 默認(rèn)都是沒(méi)有被覆蓋的
      status: 0,// 是否被選中的狀態(tài)
      icon,// 圖標(biāo)
      id: randomString(4), // 生成隨機(jī)id
      x: column * 100 + offset, //x 坐標(biāo)
      y: row * 100 + offset,// y坐標(biāo)
    }

然后再來(lái)說(shuō)算法,他的算法,本質(zhì)上其實(shí)就是限定的畫布內(nèi),隨機(jī)生成位置

在當(dāng)前這個(gè)算法中他使用一個(gè)8x8的網(wǎng)格中,生成方塊,然后利用隨機(jī)偏移量,來(lái)造成隨機(jī)堆疊的樣子

// 以下感謝大佬們提供的算法
const makeScene = (level) => {
  // 獲取當(dāng)前關(guān)卡
  const curLevel = Math.min(maxLevel, level);
  // 獲取當(dāng)前關(guān)卡應(yīng)該擁有的icon數(shù)量
  const iconPool = icons.slice(0, 2 * curLevel);
  // 算出偏移量范圍具體細(xì)節(jié)范圍
  const offsetPool = [0, 25, -25, 50, -50].slice(0, 1 + curLevel);
  //  最終的元數(shù)據(jù)數(shù)組
  const scene = [];
  // 確定范圍
  //在一般情下 translate 的偏移量,如果是百分比的話,是按照自身的寬度或者高度去計(jì)算的,所以最大的偏移范圍是百分800%
  // 然后通過(guò)Math.random 會(huì)小于百分之八百
  // 所以就會(huì)形成當(dāng)前區(qū)間的隨機(jī)數(shù)
  const range = [
    [2, 6],
    [1, 6],
    [1, 7],
    [0, 7],
    [0, 8],
  ][Math.min(4, curLevel - 1)];
  const randomSet = (icon: string) => {
    // 求偏移量
    const offset = offsetPool[Math.floor(offsetPool.length * Math.random())];
    // 偏移求列數(shù)
    const row = range[0] + Math.floor((range[1] - range[0]) * Math.random());
    // 求偏移行數(shù)
    const column = range[0] + Math.floor((range[1] - range[0]) * Math.random());
    console.log(offset, row, column);
    // 生成元數(shù)據(jù)對(duì)象
    scene.push({
      isCover: false, // 默認(rèn)都是沒(méi)有被覆蓋的
      status: 0,// 是否被選中的狀態(tài)
      icon,// 圖標(biāo)
      id: randomString(4), // 生成隨機(jī)id
      x: column * 100 + offset, //x 坐標(biāo)
      y: row * 100 + offset,// y坐標(biāo)
    });
  };

  // 如果級(jí)別高了就加點(diǎn)icon 花哨一點(diǎn)
  let compareLevel = curLevel;
  while (compareLevel > 0) {
    iconPool.push(...iconPool.slice(0, Math.min(10, 2 * (compareLevel - 5))));
    compareLevel -= 5;
  }
  // 生成元數(shù)據(jù),初始狀態(tài)下 iconPool的內(nèi)容少生 隨著增加,就會(huì)越來(lái)越難
  for (const icon of iconPool) {
    for (let i = 0; i < 6; i++) {
      randomSet(icon);
    }
  }
  // 返回元數(shù)據(jù)
  return scene;
};

解釋一下, 我們?cè)诔跏蓟臅r(shí)候, 會(huì)生成一個(gè)范圍,來(lái)初始化 他的預(yù)計(jì)位置

  const range = [
    [2, 6],
    [1, 6],
    [1, 7],
    [0, 7],
    [0, 8],
  ][Math.min(4, curLevel - 1)];

range 最后的結(jié)果,就表示格子范圍,這里是為了跟關(guān)卡結(jié)合,在初始化的時(shí)候 由于圖標(biāo)少, 所以就會(huì)在 在8x8之內(nèi)的更小的格子

例如這樣:

當(dāng)關(guān)卡越來(lái)越多的時(shí)候就會(huì)如下圖:

以為在后面關(guān)卡的時(shí)候?qū)⑺械母褡訐螡M了為8x8

那么如何計(jì)算偏移量呢?

  const randomSet = (icon: string) => {
    // 求偏移量
    const offset = offsetPool[Math.floor(offsetPool.length * Math.random())];
    // 偏移求列數(shù)
    const row = range[0] + Math.floor((range[1] - range[0]) * Math.random());
    // 求偏移行數(shù)
    const column = range[0] + Math.floor((range[1] - range[0]) * Math.random());
    console.log(offset, row, column);
    // 生成元數(shù)據(jù)對(duì)象
    scene.push({
      isCover: false, // 默認(rèn)都是沒(méi)有被覆蓋的
      status: 0,// 是否被選中的狀態(tài)
      icon,// 圖標(biāo)
      id: randomString(4), // 生成隨機(jī)id
      x: column * 100 + offset, //x 坐標(biāo)
      y: row * 100 + offset,// y坐標(biāo)
    });
  };

其實(shí)偏移量的核心就是 Math.random這個(gè)函數(shù),來(lái)生成0-1的隨機(jī)數(shù),我們需要求 offset基礎(chǔ)偏移量 row列的偏移量 column行的偏移量

由于為了導(dǎo)致位置的總體差異,和細(xì)節(jié)差異,來(lái)達(dá)到符合預(yù)期的亂序效果,所以最終他生成的坐標(biāo)需要 基礎(chǔ)偏移和行列偏移來(lái)結(jié)合

檢查是否被覆算法

檢查是否被覆蓋算法其實(shí)本質(zhì)上來(lái)說(shuō) ,就是祖?zhèn)鞯?code>碰撞檢測(cè)算法

根據(jù)是否碰撞,來(lái)計(jì)算覆蓋情況

代碼如下:

// 檢查是否被覆蓋
const checkCover = (value) => {
  // 深拷貝一份
  const updateScene = value.slice();
  // 是否覆蓋算法
  // 遍歷所有的元數(shù)據(jù)
  // 雙重for循環(huán)來(lái)找到每個(gè)元素的覆蓋情況
  for (let i = 0; i < updateScene.length; i++) {
    // 當(dāng)前item對(duì)角坐標(biāo)
    const cur = updateScene[i];
    // 先假設(shè)他都不是覆蓋的
    cur.isCover = false;
    // 如果status 不為0 說(shuō)明已經(jīng)被選中了,不用再判斷了
    if (cur.status !== 0) continue;
    // 拿到坐標(biāo)
    const { x: x1, y: y1 } = cur;
    // 為了拿到他們的對(duì)角坐標(biāo),所以要加上100
    //之所以要加上100 是由于 他的總體是800% 也就是一個(gè)格子的換算寬度是100
    const x2 = x1 + 100,
      y2 = y1 + 100;
    // 第二個(gè)來(lái)循環(huán)來(lái)判斷他的覆蓋情況
    for (let j = i + 1; j < updateScene.length; j++) {
      const compare = updateScene[j];
      if (compare.status !== 0) continue;


      const { x, y } = compare;
      // 處理交集也就是選中情況
      // 兩區(qū)域有交集視為選中
      // 兩區(qū)域不重疊情況取反即為交集
      if (!(y + 100 <= y1 || y >= y2 || x + 100 <= x1 || x >= x2)) {
        // 由于后方出現(xiàn)的元素會(huì)覆蓋前方的元素,所以只要后方的元素被選中了,前方的元素就不用再判斷了
        // 又由于雙層循環(huán)第二層從j 開始,所以不用擔(dān)心會(huì)重復(fù)判斷
        cur.isCover = true;
        break;
      }
    }
  }
  scene.value = updateScene;
};

碰撞檢測(cè)

所謂碰撞檢測(cè),就是計(jì)算兩個(gè)東西的坐標(biāo)有沒(méi)有重疊,也就是求交集

主要算法如下,就是比較他們的各個(gè)方向的位置

   function isButt(obj1,obj2){
         var l1=obj1.offsetLeft;
         var t1=obj1.offsetTop;
         var r1=l1+obj1.offsetWidth;
         var b1=t1+obj1.offsetHeight;
         var l2=obj2.offsetLeft;
         var t2=obj2.offsetTop;
         var r2=l2+obj2.offsetWidth;
         var b2=t2+obj2.offsetHeight;
       return!(r1<l2||b1<t2||r2<l1||b2<t1)
    }

覆蓋算法實(shí)現(xiàn)

覆蓋算法其實(shí)實(shí)現(xiàn)也非常簡(jiǎn)單,就是一個(gè)雙重for循環(huán) 來(lái)將每個(gè)方塊的位置做比較,做一個(gè)碰撞檢測(cè),從而能篩選出來(lái)被遮擋的方塊

值得注意的是

  • 1、j的值需要從i+1開始,為了防止已經(jīng)比較過(guò)的方塊再次比較
  • 2、由于元數(shù)據(jù)的渲染,的后方物體天然的會(huì)遮擋前方物體,所以當(dāng)碰撞檢測(cè)成功之后是只需要遮擋前方方塊即可
 for (let i = 0; i < updateScene.length; i++) {
    // 第二個(gè)來(lái)循環(huán)來(lái)判斷他的覆蓋情況
    for (let j = i + 1; j < updateScene.length; j++) {
      // 執(zhí)行碰撞檢測(cè)
    }
  }

三連匹配算法

三連匹配其實(shí)相比于前兩點(diǎn),就非常簡(jiǎn)單了

我們只需要拿到相同的方塊的icon名, 湊夠三個(gè)直接改變方塊樣式即可

// 點(diǎn)擊item
const clickSymbol = async (idx: number) => {
  // 如果已經(jīng)完成了,就不處理
  if (finished.value || animating.value) return;
  // 拷貝一份Scene
  const symbol = scene.value[idx];
  // 覆蓋了和已經(jīng)在隊(duì)列里的也不處理
  if (symbol.isCover || symbol.status !== 0) return;
  //置為可以選中狀態(tài)
  symbol.status = 1;
  queue.value.push(symbol);
  // 制造動(dòng)畫效果中防止點(diǎn)擊
  animating.value = true;
  //三百毫秒的延遲
  await waitTimeout(300);
  // 拿到與他匹配的所有icon
  const filterSame = queue.value.filter((sb) => sb.icon === symbol.icon);

  // 選中的三個(gè)配對(duì)成功表示已經(jīng)是三連了
  if (filterSame.length === 3) {
    // 由于icon的類型一樣,留下隊(duì)列中的不一樣的剩余內(nèi)容重新賦值
    queue.value = queue.value.filter((sb) => sb.icon !== symbol.icon);
    // 隱藏iocn,dom
    for (const sb of filterSame) {
      const find = scene.value.find((i) => i.id === sb.id);
      // 將他們的狀態(tài)變?yōu)? 通過(guò)opacity 屬性 來(lái)隱藏icon
      if (find) find.status = 2;
    }
  }

  // 當(dāng)格子沾滿了,那么久表示已經(jīng)失敗了
  if (queue.value.length === 7) {
    tipText.value = '失敗了'
    finished.value = true;
  }

  if (!scene.value.find((s) => s.status !== 2)) {
    // 如果完成所有關(guān)卡,那就過(guò)了所有關(guān)了
    if (level.value === maxLevel) {
      tipText.value = '完成挑戰(zhàn)';
      finished.value = true
      return;
    }
    //否則加一關(guān)
    level.value = level.value + 1;
    queue.value = []
    // 重新初始化
    checkCover(makeScene(level.value + 1));
  } else {
    // 處理覆蓋情況
    checkCover(scene.value);
  }
  // 動(dòng)畫結(jié)束
  animating.value = false;
};

以上代碼中,我們只需要 改變?cè)獢?shù)據(jù)的status的狀態(tài)值即可 ,然后再配合css的視覺(jué)效果,來(lái)達(dá)到消失的效果,其實(shí)dom 還是在頁(yè)面中,并沒(méi)有消失移除,因?yàn)樵獢?shù)據(jù)沒(méi)變

隊(duì)列區(qū)排序算法

在隊(duì)列中我們發(fā)現(xiàn)如果湊夠三個(gè)他需要排序,

比如說(shuō)在有一個(gè)叉子,就會(huì)排在米飯的前面然后消失

實(shí)現(xiàn)如下:

// 隊(duì)列區(qū)排序
watchEffect(() => {
  const cache = {};
  // 通過(guò)當(dāng)前的icon的標(biāo)識(shí),將相同的icon歸納到一塊
  // 方便后續(xù)排序
  for (const symbol of queue.value) {
    if (cache[symbol.icon]) {
      cache[symbol.icon].push(symbol);
    } else {
      cache[symbol.icon] = [symbol];
    }
  }
  const temp = [];
  for (const symbols of Object.values(cache)) {
    temp.push(...(symbols as any));
  }
  const updateSortedQueue = {};
  let x = 50;
  // 拿到更新后的隊(duì)列區(qū)數(shù)據(jù),計(jì)算權(quán)重
  for (const symbol of temp) {
    updateSortedQueue[symbol.id] = x;
    x += 100;
  }
  //賦值 ,這個(gè)是為了將選中的排序后的內(nèi)容移動(dòng)到隊(duì)列區(qū)
  sortedQueue.value = updateSortedQueue
  // 檢查覆蓋情況
  checkCover(scene.value);
})

他的實(shí)現(xiàn)原理其實(shí)就是利用緩存對(duì)隊(duì)列計(jì)算先后權(quán)重,從而計(jì)算他排序的位置,其實(shí)他的元數(shù)據(jù)或者選中順序并沒(méi)有變

只是在視覺(jué)上更改了css 的樣式

總結(jié)

到此這篇關(guān)于使用Vue3實(shí)現(xiàn)羊了個(gè)羊的算法的文章就介紹到這了,更多相關(guān)vue羊了個(gè)羊算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue中常用的縮寫方式

    vue中常用的縮寫方式

    這篇文章主要介紹了vue中常用的縮寫方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • vue:左右過(guò)渡展開折疊的組件

    vue:左右過(guò)渡展開折疊的組件

    在網(wǎng)上找了好久關(guān)于左右過(guò)渡動(dòng)畫折疊的組件,沒(méi)有合適的代碼,效果類似于element UI中的Drawer抽屜組件,只不過(guò)ele中的都是懸浮的組件,工作中遇到的很多都是占用空間的展開折疊,網(wǎng)上很多也是上下展開收起的組件,于是就自己寫了一個(gè),分享給大家,感興趣的朋友參考下吧
    2023-11-11
  • Vue中SSR的作用是什么

    Vue中SSR的作用是什么

    這篇文章主要介紹了Vue中SSR的作用及使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • Vue使用el-input自動(dòng)獲取焦點(diǎn)和二次獲取焦點(diǎn)問(wèn)題及解決

    Vue使用el-input自動(dòng)獲取焦點(diǎn)和二次獲取焦點(diǎn)問(wèn)題及解決

    這篇文章主要介紹了Vue使用el-input自動(dòng)獲取焦點(diǎn)和二次獲取焦點(diǎn)問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • vue中實(shí)現(xiàn)子組件接收父組件方法并獲取返回值

    vue中實(shí)現(xiàn)子組件接收父組件方法并獲取返回值

    這篇文章主要介紹了vue中實(shí)現(xiàn)子組件接收父組件方法并獲取返回值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • vue-cli webpack 引入swiper的操作方法

    vue-cli webpack 引入swiper的操作方法

    今天小編就為大家分享一篇vue-cli webpack 引入swiper的操作方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-09-09
  • vue踩坑日記之params傳遞參數(shù)問(wèn)題

    vue踩坑日記之params傳遞參數(shù)問(wèn)題

    這篇文章主要介紹了vue踩坑日記之params傳遞參數(shù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • vue element-ui table表格滾動(dòng)加載方法

    vue element-ui table表格滾動(dòng)加載方法

    下面小編就為大家分享一篇vue element-ui table表格滾動(dòng)加載方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • Vue中強(qiáng)制組件重新渲染的正確方法

    Vue中強(qiáng)制組件重新渲染的正確方法

    這篇文章主要介紹了Vue中強(qiáng)制組件重新渲染的正確方法,幫助大家更好的理解和使用vue框架,感興趣的朋友可以了解下
    2021-01-01
  • keep-alive保持組件狀態(tài)的方法

    keep-alive保持組件狀態(tài)的方法

    這篇文章主要介紹了keep-alive保持組件狀態(tài)的方法,幫助大家更好的理解和學(xué)習(xí)vue框架,感興趣的朋友可以了解下
    2020-12-12

最新評(píng)論

乐亭县| 长子县| 洛宁县| 崇明县| 东城区| 陆丰市| 石棉县| 东乌珠穆沁旗| 云龙县| 德令哈市| 岑溪市| 惠来县| 建瓯市| 屯昌县| 杭锦旗| 高雄市| 徐汇区| 中超| 始兴县| 札达县| 营山县| 仪征市| 西乡县| 乌什县| 弥渡县| 曲阜市| 永济市| 肇庆市| 高清| 将乐县| 无为县| 合肥市| 德兴市| 泽库县| 龙南县| 广宁县| 库伦旗| 冀州市| 东山县| 大埔县| 偃师市|