<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          初學(xué)前端用代碼實(shí)現(xiàn)一個(gè)網(wǎng)頁(yè)老虎機(jī)游戲

          共 18594字,需瀏覽 38分鐘

           ·

          2021-01-13 14:00

          作者:紅領(lǐng)巾小莫

          https://juejin.cn/post/6890206996008534030

          簡(jiǎn)介

          前兩天小編在B站看到一個(gè)AE MG動(dòng)畫(huà),動(dòng)畫(huà)的內(nèi)容如下:

          這個(gè)動(dòng)畫(huà)還是挺有意思的,但是小編第一個(gè)反應(yīng)這要是哪一天某位ui姐姐或產(chǎn)品姐姐給小編提了這樣子的需求,那小編豈不是當(dāng)場(chǎng)要自閉?我本著自己的好奇心,實(shí)現(xiàn)了一個(gè)簡(jiǎn)易版的老虎機(jī):

          老虎機(jī)的整體外觀樣式還是比較好寫(xiě)的,對(duì)老虎機(jī)的外觀布局如果有興趣的話可以直接參考代碼,小編就不一一介紹了。文章主要介紹的是老虎機(jī)中間三個(gè)小格子和機(jī)身動(dòng)畫(huà)的一些實(shí)現(xiàn)要點(diǎn)。(讀者想實(shí)操的話也可以自己找一張背景圖當(dāng)作老虎機(jī)的背景,這里小編只是自己好奇而已所以就用各種基礎(chǔ)布局和樣式實(shí)現(xiàn)的老虎機(jī))

          • 格子中數(shù)字的真面目是什么?

          • 數(shù)字列表滾動(dòng)前的要點(diǎn)

          • 格子中的數(shù)字列表是怎么滾動(dòng)的?

          • “無(wú)限滾動(dòng)”是怎么實(shí)現(xiàn)的?

          • 第二個(gè)和第三個(gè)格子的延遲滾動(dòng)怎么實(shí)現(xiàn)的?

          • 隨機(jī)的滾動(dòng)結(jié)果是怎么實(shí)現(xiàn)的?

          • 游戲機(jī)是怎么抖動(dòng)起來(lái)的?

          • 重置游戲的實(shí)現(xiàn)及要注意的點(diǎn)

          • 怎么獲取老虎機(jī)的游戲結(jié)果?


          格子中數(shù)字的真面目是什么?

          這個(gè)問(wèn)題其實(shí)很簡(jiǎn)單,格子中的內(nèi)容單純只是一個(gè)ul列表,我是只是給格子添加了overflow:hidden使格子外的數(shù)字進(jìn)行了隱藏。咱們先把一個(gè)格子的數(shù)字列表拿出來(lái)講,我們可以看到整個(gè)滾動(dòng)過(guò)程中只有“1~6”共6個(gè)數(shù)字,我們可以先把這6個(gè)數(shù)字的列表給實(shí)現(xiàn)下。

          我們利用的是ul 和 li標(biāo)簽做出的列表,在布局中小編只寫(xiě)了ul,雖然效果圖中只有1~6 6種情況,但是后面可能會(huì)有更多的游戲結(jié)果選項(xiàng),所以li標(biāo)簽就不寫(xiě)死在頁(yè)面中,li標(biāo)簽通過(guò)javaScript的形式添加到ul標(biāo)簽中。

          (這里為了方便讀者讀懂代碼我先解釋一下,小編在一開(kāi)始考慮老虎機(jī)里面的內(nèi)容以后可能是圖片而不是數(shù)字,所以在很多地方變量或者className的命名都名為與“images”相關(guān)。)

            
           // 初始的選項(xiàng)列表  let initImagesArr = [6, 5, 4, 3, 2, 1];  // 獲取第一個(gè)ul列表  const firstImagesList = document.getElementsByClassName('images')[0];   // 構(gòu)造列表li添加到ul標(biāo)簽中去  initImagesArr.forEach(item => {    const li = document.createElement('li');    li.innerHTML = item;    firstImagesList.appendChild(li);  });

          這樣子我們就可以獲得這樣的一個(gè)ul列表:

           
          • 6
          • 5
          • 4
          • 3
          • 2
          • 1

          同理,第二個(gè)格子和第三個(gè)格子也可以利用相同的方式構(gòu)造出相同的數(shù)字列表。通過(guò)給ul和li添加樣式之后的效果如圖:


          數(shù)字列表滾動(dòng)前的要點(diǎn)

          tip: 讀者可以留意一下,firstImagesList代表的是第一個(gè)數(shù)字列表,secondsImagesList為第二個(gè)數(shù)字列表,thirdImagesList為第三個(gè)數(shù)字列表。其中代碼:

            const firstImagesList = document.getElementsByClassName('images')[0];  const secondsImagesList = document.getElementsByClassName('images')[1];??const?thirdImagesList?=?document.getElementsByClassName('images')[2];
          我們可以從動(dòng)畫(huà)中看出數(shù)字是從上往下開(kāi)始滾動(dòng)的。其實(shí)滾動(dòng)的原理利用的是CSS3的transform:translateY()進(jìn)行移動(dòng)。那有人可能就有疑問(wèn)了(小編你不是說(shuō)從上往下滾動(dòng)嗎,按照你列表這樣的布局你從上往下不是一移動(dòng)就結(jié)束了嗎)。

          所以我們?cè)诹斜硪苿?dòng)之前要做一件事情,我們要把這個(gè)列表的初始化布局給調(diào)整一下,將全部列表都向上移動(dòng),使數(shù)字“1”移動(dòng)到格子中去。

          我們先聲明一個(gè)初始化三個(gè)數(shù)字列表定位方法,其中參數(shù)startTranslateYHeight代表整個(gè)列表要向上移動(dòng)的距離。

          function initPosition(startTranslateYHeight) {    firstImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;    secondsImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;    thirdImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;}

          有了移動(dòng)的方法之后,我們要計(jì)算出列表要向上移動(dòng)的距離startTranslateYHeight。獲取向上移動(dòng)距離的步驟包括:

          1. 聲明列表單獨(dú)一項(xiàng)也就是單個(gè)li的高度,已知li標(biāo)簽的高度為136px;

          2. 獲取一列的高度,也就是整個(gè)ul標(biāo)簽的高度,這個(gè)三個(gè)列表的高度都一樣大,所以我們?nèi)〉谝粋€(gè)列表利用dom的內(nèi)置屬性scrollHeight獲得列表的高度;

          3. 因?yàn)檎麄€(gè)列表向上移動(dòng)到最后數(shù)字1會(huì)留在格子中,列表中全部有6個(gè)數(shù)字,我們只需要向上移動(dòng)5個(gè)數(shù)字的高度即可。也就是整個(gè)整個(gè)列表的高度減去一個(gè)li標(biāo)簽的高度就是我們要列表向上移動(dòng)的距離。

          const imageHeight = 136;  const listHeight = firstImagesList.scrollHeight;??const?startTranslateYHeight?=?-?listHeight?+?imageHeight;

          有了向上移動(dòng)的具體距離后,我們只要執(zhí)行我們的初始化定位方法initPosition()方法即可。


          格子中的數(shù)字列表是怎么滾動(dòng)的?

          前面我們也提到了其實(shí)滾動(dòng)的原理利用的是CSS3的transform:translateY()進(jìn)行移動(dòng)。因?yàn)槲覀冇螒蚴峭ㄟ^(guò)點(diǎn)擊手柄開(kāi)始的,所以我們給手柄添加一個(gè)點(diǎn)擊事件,并在事件中給列表進(jìn)行滾動(dòng),我們暫時(shí)默認(rèn)滾動(dòng)到最后一個(gè)數(shù)字,不考慮隨機(jī)結(jié)果的情況。

          如果只是滾動(dòng)到最后一個(gè)數(shù)字那還是比較容易的,那我們只需要將向上移動(dòng)的距離還原為0就可以了,這樣子就能達(dá)到向下移動(dòng)的效果。

          // 點(diǎn)擊游戲手柄開(kāi)始游戲的方法function start() {  firstImagesList.style.transform = 'translateY(0)';  secondsImagesList.style.transform = 'translateY(0)';  thirdImagesList.style.transform = 'translateY(0)';}

          我們來(lái)看一下現(xiàn)階段的效果:

          是不是少了點(diǎn)什么對(duì)吧?沒(méi)錯(cuò),少了滾動(dòng)動(dòng)畫(huà)。我們只需要在游戲開(kāi)始時(shí)給列表加上過(guò)渡效果即可??赡苡腥藭?huì)問(wèn)為什么要在游戲開(kāi)始時(shí)再加而不是一開(kāi)始寫(xiě)樣式時(shí)先寫(xiě)上transtion過(guò)渡。原因是這樣子阿:因?yàn)樾【幒罄m(xù)要考慮到重置游戲的問(wèn)題,重置過(guò)程列表會(huì)回到最開(kāi)始的定位處,如果說(shuō)重置過(guò)程也有過(guò)渡樣式那是不太合理的,為了能夠保證過(guò)渡樣式是可控的小編就定義了一個(gè)添加過(guò)渡的方法,還有一個(gè)刪除過(guò)渡的方法,方便我們想要有過(guò)渡動(dòng)畫(huà)就加上,不想有過(guò)渡動(dòng)畫(huà)就刪除。

          下面的代碼意思就是分別給每個(gè)列表添加/刪除過(guò)渡樣式類(lèi)名(className),刪除過(guò)渡我們會(huì)在重置動(dòng)畫(huà)中使用到。

             //過(guò)渡效果   .transtion {    transition: all ease 2s;  }
            // 全部列表添加過(guò)渡效果  function addTranstion() {    firstImagesList.classList.add('transtion');    secondsImagesList.classList.add('transtion');    thirdImagesList.classList.add('transtion');  }
          // 全部列表刪除過(guò)渡效果 function removeTranstion(imagesList) { firstImagesList.classList.remove('transtion'); secondsImagesList.classList.remove('transtion'); thirdImagesList.classList.remove('transtion'); }

          然后我們只需要只開(kāi)始游戲方法中調(diào)用添加過(guò)渡方法即可:

          function start() {  // 游戲開(kāi)始給全部數(shù)字列表添加過(guò)渡效果  addTranstion();  firstImagesList.style.transform = 'translateY(0)';  secondsImagesList.style.transform = 'translateY(0)';  thirdImagesList.style.transform = 'translateY(0)';}

          到這里我們已經(jīng)實(shí)現(xiàn)了數(shù)字列表的滾動(dòng)效果,但是我們只是做了個(gè)簡(jiǎn)單的從1~6的滾動(dòng),并沒(méi)有做到從頭開(kāi)始的效果。簡(jiǎn)單來(lái)說(shuō)就是滾動(dòng)得沒(méi)有像效果圖中那么“持久”。我們接下來(lái)就是來(lái)實(shí)現(xiàn)一下“從頭開(kāi)始”,“無(wú)限滾動(dòng)”的效果。


          “無(wú)限滾動(dòng)”是怎么實(shí)現(xiàn)的?

          效果圖中我們可以看到當(dāng)數(shù)字6滾動(dòng)結(jié)束之后應(yīng)該會(huì)重?cái)?shù)字1開(kāi)始重新滾動(dòng),話不多說(shuō)我們直接揭開(kāi)謎底。

          可能有的小伙伴看到這里就明白小編是怎么實(shí)現(xiàn)的了。其實(shí)我這里并沒(méi)有實(shí)現(xiàn)所謂的“無(wú)限滾動(dòng)”,我只是把初始化的數(shù)組按倍數(shù)給擴(kuò)充了很多分,使得整個(gè)列表變得非常得長(zhǎng),以至于在短時(shí)間內(nèi)的過(guò)渡效果中整個(gè)列表看著像是在“無(wú)限滾動(dòng)”。

          [6,5,4,3,2,1,6,5,4,3,2,1,6,5,4,3,2,1,6,5,4,3,2,1,6,5,4,3,2,1,6,5,4,3,2,1]
            // 初始的選項(xiàng)列表  let initImagesArr = [6, 5, 4, 3, 2, 1];  let imagesArr = [6, 5, 4, 3, 2, 1];  // 加長(zhǎng)整個(gè)選項(xiàng)列表,以完成一個(gè)虛假的無(wú)限滾動(dòng)的效果  new Array(20).fill('').forEach(() => {    imagesArr = imagesArr.concat(...initImagesArr);??})

          那么此時(shí)前面添加li標(biāo)簽的代碼就得修改一下了,將initImageArr修改為新的選項(xiàng)列表imagesArr

             // 構(gòu)造列表li添加到ul標(biāo)簽中去  imagesArr.forEach(item => {    const li = document.createElement('li');    li.innerHTML = item;    firstImagesList.appendChild(li);??});

          至此我們虛假的無(wú)限滾動(dòng)就已經(jīng)實(shí)現(xiàn)完成了(如果打滅了你們對(duì)無(wú)限滾動(dòng)的期待的話請(qǐng)不要打小編,小編心里也苦,真正的無(wú)限滾動(dòng)好像不太好寫(xiě),有感興趣的小伙伴要是知道怎么無(wú)限滾動(dòng)就告訴我哈,小編也來(lái)學(xué)習(xí)學(xué)習(xí))。


          第二個(gè)和第三個(gè)格子的延遲滾動(dòng)怎么實(shí)現(xiàn)的?

          無(wú)限滾動(dòng)介紹完之后我們來(lái)介紹一下延遲滾動(dòng)的問(wèn)題,我們可以看到效果圖中第二個(gè)格子是等第一個(gè)格子滾動(dòng)一小會(huì)兒后才開(kāi)始滾動(dòng)的,第三個(gè)格子也是一樣的。

          其實(shí)延遲滾動(dòng)實(shí)現(xiàn)也很簡(jiǎn)單,我們只需要給第二個(gè)數(shù)字列表和第三個(gè)數(shù)字列表各自的滾動(dòng)方法中設(shè)置個(gè)定時(shí)器即可。

            // 點(diǎn)擊游戲手柄開(kāi)始游戲的方法  function start() {    // 游戲開(kāi)始給全部數(shù)字列表添加過(guò)渡效果    addTranstion();    firstImagesList.style.transform = 'translateY(0)';    // 列表2延遲0.5s后滾動(dòng)    timeout1 = setTimeout(() => {      secondsImagesList.style.transform = 'translateY(0)';    },500)    // 列表3延遲1s后滾動(dòng)    timeout2 = setTimeout(() => {      thirdImagesList.style.transform = 'translateY(0)';    },1000)??}


          隨機(jī)的滾動(dòng)結(jié)果是怎么實(shí)現(xiàn)的?

          隨機(jī)的滾動(dòng)結(jié)果解釋起來(lái)可能會(huì)比較難以理解一點(diǎn)。我們?cè)倩仡櫼幌拢谏厦嫖覀儗?shí)現(xiàn)向下滾動(dòng)的原理是將向上移動(dòng)的距離還原為0('translateY(0))來(lái)實(shí)現(xiàn)的。那試想一下如果我們還原的結(jié)果不是0,而是一個(gè)數(shù)字的高度呢?

          Tip: 一個(gè)數(shù)字的高度也就是一個(gè)li標(biāo)簽的高度,前面我們已知一個(gè)li標(biāo)簽高度是136px

          我們來(lái)改寫(xiě)代碼試試:

            firstImagesList.style.transform = 'translateY(-136px)';  // 列表2延遲0.5s后滾動(dòng)  timeout1 = setTimeout(() => {    secondsImagesList.style.transform = 'translateY(-136px)';  },500)  // 列表3延遲1s后滾動(dòng)  timeout2 = setTimeout(() => {    thirdImagesList.style.transform = 'translateY(-136px)';??},1000)

          效果如下:

          我們可以看到,如果我們將定位只還原到translateY(-136px),那滾動(dòng)的結(jié)果會(huì)是5。以此類(lèi)推,如果我們只還原到0、-136、-136 * 2、-136 * 3、-136 * 4、-136 * 5(單位都為px)、那么我們就可以在數(shù)字列表滾動(dòng)中得到6,5,4,3,2,1共6中結(jié)果。

          現(xiàn)在我們已經(jīng)能夠通過(guò)改變不同的還原距離translateY()從而達(dá)到滾動(dòng)結(jié)果的不同,那還有一個(gè)問(wèn)題,從上面6個(gè)數(shù)中隨機(jī)出一個(gè)數(shù)來(lái)怎么做呢?滾動(dòng)的結(jié)果在這里不應(yīng)該是由我們?nèi)藶榭刂频摹P【幭肓艘幌?,還好這里最起碼的要求是結(jié)果應(yīng)該是從-136的倍數(shù)0,1,2,3,4,5種隨機(jī)出一個(gè)數(shù)來(lái)。我們通過(guò)倍數(shù)的變化就能獲取到相應(yīng)的隨機(jī)值。

          這里我利用了js種Math對(duì)象的Math.random()方法,Math.random()方法會(huì)返回介于 0(包含) ~ 1(不包含) 之間的一個(gè)隨機(jī)數(shù), 那如果我將Math.random的結(jié)果乘以6,那我不就得到0~6(不包含6)之間的一個(gè)隨機(jī)數(shù),并且我將獲取的隨機(jī)數(shù)通過(guò)Math.floor()做一個(gè)向下取整,那我不就得到0,1,2,3,4,5的隨機(jī)數(shù)了。

          目前整個(gè)游戲的開(kāi)始方法整理如下:

          //  初始的選項(xiàng)列表initImagesArr中有6個(gè)值,也就是單列數(shù)據(jù)列表總的情況會(huì)有6種
          中間內(nèi)容省略。。。
           // 單個(gè)數(shù)字的高度,即單個(gè)li標(biāo)簽的高度 const imageHeight = 136; // resultNum為滾動(dòng)結(jié)果情況的個(gè)數(shù),這里有6個(gè)數(shù)字,也就是有6種情況 // translateY(${-imageHeight * radom1}px)為列表1最終還原的位置,以此類(lèi)推 function start(resultNum) {  // 游戲開(kāi)始給全部數(shù)字列表添加過(guò)渡效果  addTranstion();  // 每個(gè)列表滾動(dòng)的隨機(jī)結(jié)果0~resultNum  let radom1 = Math.floor(Math.random()*resultNum);  let radom2 = Math.floor(Math.random()*resultNum);  let radom3 = Math.floor(Math.random()*resultNum);  // 列表1開(kāi)始滾動(dòng)  firstImagesList.style.transform = `translateY(${-imageHeight * radom1}px)`;  // 列表2延遲0.5s后滾動(dòng)  timeout1 = setTimeout(() => {    secondsImagesList.style.transform = `translateY(${-imageHeight * radom2}px)`;  },500)  // 列表3延遲1s后滾動(dòng)  timeout2 = setTimeout(() => {    thirdImagesList.style.transform = `translateY(${-imageHeight * radom3}px)`;  },1000)}

          注意點(diǎn)1:方法中resultNum為單個(gè)數(shù)字列表滾動(dòng)結(jié)果的全部可能性。另外因?yàn)槿拷Y(jié)果的情況總數(shù)由初始數(shù)字列表initImagesArr: [1,2,3,4,5,6]中的數(shù)字個(gè)數(shù)所決定的,所以只需要將initImagesArr.length作為參數(shù)傳給start()方法即可。

          注意點(diǎn)2:三個(gè)數(shù)字列表的隨機(jī)結(jié)果都不同,其中radom1為列表1的隨機(jī)結(jié)果,radom2為列表2的隨機(jī)結(jié)果,radom3為列表3的隨機(jī)結(jié)果。Math.random()*resultNum的值為 0 * resultNum ~ 1 * resultNum(不包含1 * resultNum)。本例中resultNum為6,則結(jié)果為0 ~ 5.999999999 經(jīng)過(guò)Math.floor()向下取整過(guò)后的結(jié)果為0 ~ 5。

          經(jīng)過(guò)以上的處理之后,我們的隨機(jī)結(jié)果就已經(jīng)成功實(shí)現(xiàn)了。


          游戲機(jī)是怎么抖動(dòng)起來(lái)的?

          前面可能會(huì)復(fù)雜一點(diǎn),這里就我們聊個(gè)稍微簡(jiǎn)單易懂的東西。從效果圖中我們可以看出老虎機(jī)從開(kāi)始游戲到游戲快結(jié)束時(shí)一直是在抖動(dòng)的,關(guān)于這個(gè)我也給大家稍微分享一下怎么實(shí)現(xiàn)的。

          其實(shí)就一個(gè)東西,加個(gè)動(dòng)畫(huà)。這里我先直接貼上代碼:

            .shake {    animation: shake 0.1s infinite;  }
          @keyframes shake { 25% { left: 49.7%; } 50% { top: 49.7%; } 75% { left: 50%; } 100% { top: 50%; }??}

          在寫(xiě)樣式時(shí)小編通過(guò)相對(duì)定位的形式position:absoulte 配合{left:50%;top:50%;translate(-50%,-50%)}的形式實(shí)現(xiàn)老虎機(jī)相對(duì)可視區(qū)域水平垂直居中對(duì)齊的效果。

          // 老虎機(jī)相對(duì)可視區(qū)域水平垂直居中對(duì)齊的效果。.machine {  position: absolute;  top: 50%;  left: 50%;  transform: translate(-50%,-50%);}

          解釋一下代碼,小編通過(guò)給整個(gè)老虎機(jī)從各個(gè)方向都移動(dòng)一下,并且以很快的速度完成(這里用的是0.1s完成的動(dòng)畫(huà)),動(dòng)畫(huà)的循環(huán)次數(shù)為無(wú)限次infinite,從而實(shí)現(xiàn)了老虎機(jī)一直在抖的效果。

          不過(guò)要稍微留意一點(diǎn),我們這里抖動(dòng)應(yīng)該也是要可控的,因?yàn)槔匣C(jī)在游戲快結(jié)束時(shí)會(huì)停止抖動(dòng)。跟我們之前做過(guò)渡效果可控的方式一樣,我們也給動(dòng)畫(huà)聲明一個(gè)添加抖動(dòng)和移除抖動(dòng)的方法:

            // 給老虎機(jī)添加抖動(dòng)效果  function startShake() {    document.getElementsByClassName('machine')[0].classList.add('shake');  } // 移除老虎機(jī)抖動(dòng)效果  function stopShake() {    document.getElementsByClassName('machine')[0].classList.remove('shake');??}

          之后我們?cè)谟螒蜷_(kāi)始的時(shí)候調(diào)用startShake(),然后在游戲快結(jié)束時(shí)調(diào)用stopShake()。不過(guò)在下面代碼我們可以看到移除抖動(dòng)效果是在2.6s之后才執(zhí)行的,原因是第三個(gè)數(shù)字列表需要等到游戲開(kāi)始1s才開(kāi)始滾動(dòng),而且滾動(dòng)的過(guò)渡時(shí)間為2s,那等到第三個(gè)數(shù)字列表滾動(dòng)到結(jié)束總共需要3s,這里小編想要在第三個(gè)數(shù)字列表滾動(dòng)結(jié)束之前將老虎機(jī)停止抖動(dòng),所以將移除抖動(dòng)方法在游戲開(kāi)始2.6s之后才執(zhí)行。

           
          // 單個(gè)數(shù)字的高度,即單個(gè)li標(biāo)簽的高度 const imageHeight = 136; function start(resultNum) {  // 游戲開(kāi)始給全部數(shù)字列表添加過(guò)渡效果  addTranstion();  // 游戲開(kāi)始給老虎機(jī)添加抖動(dòng)效果  startShake();  // 每個(gè)列表滾動(dòng)的隨機(jī)結(jié)果0~resultNum  let radom1 = Math.floor(Math.random()*resultNum);  let radom2 = Math.floor(Math.random()*resultNum);  let radom3 = Math.floor(Math.random()*resultNum);  // 列表1開(kāi)始滾動(dòng)  firstImagesList.style.transform = `translateY(${-imageHeight * radom1}px)`;  // 列表2延遲0.5s后滾動(dòng)  timeout1 = setTimeout(() => {    secondsImagesList.style.transform = `translateY(${-imageHeight * radom2}px)`;  },500)  // 列表3延遲1s后滾動(dòng)  timeout2 = setTimeout(() => {    thirdImagesList.style.transform = `translateY(${-imageHeight * radom3}px)`;  },1000)  // 老虎機(jī)在延遲2.6妙后移除抖動(dòng)效果  timeout3 = setTimeout(() => {    stopShake();  },2600)}

          重置游戲的實(shí)現(xiàn)及要注意的點(diǎn)

          老虎機(jī)從開(kāi)始游戲到結(jié)束游戲的整個(gè)環(huán)節(jié)我們都已經(jīng)實(shí)現(xiàn)完成了。但是呢,小編還想再玩一把,然后我在第二次點(diǎn)擊開(kāi)始手柄之前就想到了這個(gè)老虎機(jī)存在的缺陷,總結(jié)起來(lái)包括以下幾點(diǎn):

          • 游戲從開(kāi)始到結(jié)束之后,再次點(diǎn)擊開(kāi)始手柄,應(yīng)該將游戲進(jìn)行重置

          • 重置游戲的過(guò)程中不應(yīng)該出現(xiàn)過(guò)渡效果

          • 重置游戲的過(guò)程中機(jī)器不應(yīng)該繼續(xù)在搖晃

          • 如果將游戲進(jìn)行重置了,第一次開(kāi)始游戲方法中的定時(shí)器方法應(yīng)該清空。

          小編給這臺(tái)老虎機(jī)做了個(gè)設(shè)定,當(dāng)游戲手柄點(diǎn)擊第一下時(shí),游戲開(kāi)始。當(dāng)游戲手柄點(diǎn)擊第二下時(shí),游戲要結(jié)束并重置游戲。當(dāng)游戲手柄點(diǎn)擊第三下時(shí),游戲又再度重新開(kāi)始。

          這里可能有個(gè)疑惑就是開(kāi)始游戲手柄的點(diǎn)擊事件只綁定了一個(gè)開(kāi)始游戲start()的方法,那怎么判斷游戲是開(kāi)始還是重置?

          中間內(nèi)容省略。。。

          這里小編給start寫(xiě)了個(gè)flag,這個(gè)當(dāng)flag為true時(shí),會(huì)執(zhí)行開(kāi)始游戲的方法,當(dāng)flag為false時(shí),會(huì)執(zhí)行重置游戲的方法,每次點(diǎn)擊時(shí)將flag的值重新賦為flag的反向值即可。

            
          // 游戲是否開(kāi)始 true為開(kāi)始游戲,false為重置游戲  let isStart = false;  function start(imagesArrLength) {    // flag取反    isStart = !isStart;    // 開(kāi)始游戲    if (isStart) {      // 開(kāi)始游戲就給列表加過(guò)渡效果      addTranstion();      startShake();
          // 每個(gè)列表滾動(dòng)的隨機(jī)結(jié)果 let radom1 = Math.floor(Math.random()*imagesArrLength); let radom2 = Math.floor(Math.random()*imagesArrLength); let radom3 = Math.floor(Math.random()*imagesArrLength);
          firstImagesList.style.transform = `translateY(${-imageHeight * radom1}px)`;
          // 列表2延遲0.5s后滾動(dòng) timeout1 = setTimeout(() => { secondsImagesList.style.transform = `translateY(${-imageHeight * radom2}px)`; },500)
          // 列表3延遲1s后滾動(dòng) timeout2 = setTimeout(() => { thirdImagesList.style.transform = `translateY(${-imageHeight * radom3}px)`; },1000)
          timeout3 = setTimeout(() => { stopShake(); },2600) }else { // 重置游戲,這里實(shí)現(xiàn)重置游戲的方法 }??}

          那重置游戲需要我們做什么呢?

          1、首先當(dāng)然是將所有的數(shù)字列表回到初始位置。我們?cè)谇懊妗皵?shù)字列表滾動(dòng)前的要點(diǎn)”中已經(jīng)實(shí)現(xiàn)了初始化定位方法initPosition()方法,在上面else代碼塊中我們只要調(diào)用initPosition(startTranslateYHeight)即可將全部數(shù)字列表回到初始的位置。Tip:在前面“數(shù)字列表滾動(dòng)前的要點(diǎn)”中我們已經(jīng)介紹了startTranslateYHeight為列表向上移動(dòng)的距離。

            function initPosition(startTranslateYHeight) {      firstImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;      secondsImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;      thirdImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;??}

          2、 重置游戲時(shí)數(shù)字列表在回到初始位置的過(guò)程中,由于游戲開(kāi)始過(guò)程中數(shù)字列表添加了過(guò)渡動(dòng)畫(huà),會(huì)導(dǎo)致數(shù)字列表在回到初始位置的過(guò)程也會(huì)存在過(guò)渡動(dòng)畫(huà),因此我們需要調(diào)用之前先聲明好的removeTranstion()來(lái)刪除全部數(shù)字列表的過(guò)渡效果。

          3、 重置游戲時(shí)由于第一次開(kāi)始游戲過(guò)程中給老虎機(jī)添加了抖動(dòng)效果,重置時(shí)應(yīng)該移除抖動(dòng)效果。在else代碼塊中調(diào)用stopShake()即可。

          4、 假如在開(kāi)始游戲后在很短的時(shí)間內(nèi)又點(diǎn)擊了重置游戲,這時(shí)候開(kāi)始游戲中未執(zhí)行的定時(shí)器中的方法應(yīng)該通過(guò)clearTimeout()給予一一清除,否則在重置游戲時(shí)還是會(huì)執(zhí)行開(kāi)始游戲中的方法。

          那么else代碼塊中的代碼應(yīng)該為:

          else {  clearTimeout(timeout1);  clearTimeout(timeout2);  clearTimeout(timeout3);  // 停止抖動(dòng)  stopShake();  // 重置時(shí)因?yàn)榱斜頃?huì)重新移動(dòng)到初始位置,所以要清除掉過(guò)渡效果  removeTranstion();  // 各個(gè)列表回到最初的位置  initPosition(startTranslateYHeight);}

          怎么獲取老虎機(jī)的游戲結(jié)果

          是個(gè)游戲總會(huì)有個(gè)結(jié)果的。這里我們實(shí)現(xiàn)在老虎機(jī)游戲結(jié)束之后,將游戲結(jié)果給打印出來(lái)。由于我們的結(jié)果是通過(guò)radom1,radom2,radom3隨機(jī)結(jié)果得出,當(dāng)radom1為0時(shí)結(jié)果為6,random1為1時(shí)結(jié)果為5,依次類(lèi)推。我們可以得到最終的結(jié)果應(yīng)該為 initImagesArr[radom1], initImagesArr[radom2], initImagesArr[radom3]。

          前面我們也有提到游戲結(jié)束的整個(gè)過(guò)程需要經(jīng)歷3s,所以我們定義一個(gè)定時(shí)器在開(kāi)始游戲3s后執(zhí)行并打印出游戲結(jié)果即可。

          代碼附錄

          html:

          <body>  <div class="machine">    <div class="machine-body">      <div class="rotary-table">        <div class="screen">          <ul class="images">ul>        div>        <div class="screen">          <ul class="images">ul>        div>        <div class="screen">          <ul class="images">ul>        div>      div>      <div class="machine-logo">        <img src="./index.png">      div>    div>    <div class="base">      <div class="handle" onclick="start(initImagesArr.length)">        <div class="handle-ball">div>        <div class="handle-bar">div>        <div class="handle-shaft">div>      div>    div>  div>body>

          scss:

          .machine {  width: 484px;  height: 513px;  position: absolute;  top: 50%;  left: 50%;  transform: translate(-50%,-50%);  display: flex;  .machine-body {    width: 400px;    background-color: #c8d0e3;    height: 513px;    border: 14px solid #2e2a27;    border-radius: 34px;    box-sizing: border-box;    .rotary-table {      width: 278px;      border: 16px solid #2e2a27;      border-radius: 16px;      margin: 54px auto 0;      display: flex;      .screen {        width: 82px;        height: 136px;        overflow: hidden;        .images {          list-style: none;          margin: 0;          padding: 0;          border: none;          li {            margin: 0;            padding: 0;            border: 0;            text-align: center;            height: 136px;            line-height: 136px;            font-size: 30px;            background-color: wheat;          }        }        .transtion {          transition: all ease 2s;        }      }    }    .rotary-table :nth-child(1) {      border-right: 16px solid #2e2a27;    }    .rotary-table :nth-child(2) {      border-right: 16px solid #2e2a27;    }    .machine-logo {      width: 240px;      margin: 20px auto 0;      img {        width: 100%;        height: auto;      }    }  }  .base {    width: 34px;    height: 130px;    background-color: #b1b8d4;    border: 14px solid #2e2a27;    border-left: none;    box-sizing: border-box;    border-top-right-radius: 24px;    border-bottom-right-radius: 24px;    transform: translateY(250px);    position: relative;    .handle {      width: 64px;      position: absolute;      left: 30px;      bottom: 28px;      cursor: pointer;      .handle-ball {        width: 34px;        height: 34px;        background-color: #ff6169;        border-radius: 50%;        border: 15px solid #2e2a27;        transform: translateY(2px);      }      .handle-bar {        width: 16px;        height: 106px;        margin: 0 auto;        background-color: #2e2a27;        transform: translateY(1px);      }      .handle-shaft {        width: 56px;        height: 48px;        border: 15px solid #2e2a27;        border-left: none;        border-top-right-radius: 25px;        border-bottom-right-radius: 25px;        background-color: #c8d0e3;        box-sizing: border-box;        margin: 0 auto;      }    }  }}
          .shake { animation: shake 0.1s infinite;}
          @keyframes shake { 25% { left: 49.7%; } 50% { top: 49.7%; } 75% { left: 50%; } 100% { top: 50%; }}

          javaScript:

            
          // 以下的images命名可能是考慮老虎機(jī)里的內(nèi)容可能會(huì)被替換成水果等其他圖片而非數(shù)字,所以將相關(guān)的內(nèi)容變量命名為與images相關(guān)  // 頁(yè)面剛加載時(shí)三個(gè)列表的初始定位  function initPosition(startTranslateYHeight) {    firstImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;    secondsImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;    thirdImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;  }
          // 全部列表添加過(guò)渡效果 function addTranstion() { firstImagesList.classList.add('transtion'); secondsImagesList.classList.add('transtion'); thirdImagesList.classList.add('transtion'); }
          // 全部列表刪除過(guò)渡效果 function removeTranstion(imagesList) { firstImagesList.classList.remove('transtion'); secondsImagesList.classList.remove('transtion'); thirdImagesList.classList.remove('transtion'); }
          // 給老虎機(jī)添加搖晃動(dòng)畫(huà) function startShake() { document.getElementsByClassName('machine')[0].classList.add('shake'); } // 停止老虎機(jī)搖晃動(dòng)畫(huà) function stopShake() { document.getElementsByClassName('machine')[0].classList.remove('shake'); }
          // 點(diǎn)擊第一個(gè)是開(kāi)始,點(diǎn)擊第二次是充值游戲 // startTranslateYHeight 列表的初始化時(shí)translateY的距離 // imageHeight列表的每一項(xiàng)的高度 // 列表數(shù)組的長(zhǎng)度 function start(imagesArrLength) { isStart = !isStart; // 開(kāi)始游戲 if (isStart) { // 開(kāi)始游戲就給列表加過(guò)渡效果 addTranstion(); startShake();
          // 每個(gè)列表滾動(dòng)的隨機(jī)結(jié)果 let radom1 = Math.floor(Math.random()*imagesArrLength); let radom2 = Math.floor(Math.random()*imagesArrLength); let radom3 = Math.floor(Math.random()*imagesArrLength);
          firstImagesList.style.transform = `translateY(${-imageHeight * radom1}px)`;
          // 列表2延遲0.5s后滾動(dòng) timeout1 = setTimeout(() => { secondsImagesList.style.transform = `translateY(${-imageHeight * radom2}px)`; },500)
          // 列表3延遲1s后滾動(dòng) timeout2 = setTimeout(() => { thirdImagesList.style.transform = `translateY(${-imageHeight * radom3}px)`; },1000) // 延遲2.6秒后停止抖動(dòng) timeout3 = setTimeout(() => { stopShake(); },2600) // 游戲結(jié)束后打印結(jié)果 timeout4 = setTimeout(() => { console.log(initImagesArr[radom1],initImagesArr[radom2],initImagesArr[radom3]); },3000) // 重置游戲 }else { // 取消上一次未執(zhí)行完的方法 clearTimeout(timeout1); clearTimeout(timeout2); clearTimeout(timeout3); clearTimeout(timeout4); stopShake(); // 重置時(shí)因?yàn)榱斜頃?huì)重新移動(dòng)到初始位置,所以要清除掉過(guò)渡效果 removeTranstion(); // 各個(gè)列表回到最初的位置 initPosition(startTranslateYHeight); } }
          // 初始的選項(xiàng)列表 let initImagesArr = [6, 5, 4, 3, 2, 1]; let imagesArr = [6, 5, 4, 3, 2, 1]; // 加長(zhǎng)整個(gè)選項(xiàng)列表,以完成一個(gè)虛假的滾動(dòng)的效果 new Array(20).fill('').forEach(() => { imagesArr = imagesArr.concat(...initImagesArr); })
          // 獲取第一個(gè)列表的dom const firstImagesList = document.getElementsByClassName('images')[0]; const secondsImagesList = document.getElementsByClassName('images')[1]; const thirdImagesList = document.getElementsByClassName('images')[2]; // 構(gòu)造列表li添加到ul標(biāo)簽中去 imagesArr.forEach(item => { const li = document.createElement('li'); const li2 = document.createElement('li'); const li3 = document.createElement('li'); li.innerHTML = item; li2.innerHTML = item; li3.innerHTML = item; firstImagesList.appendChild(li); secondsImagesList.appendChild(li2); thirdImagesList.appendChild(li3); });

          // 列表單獨(dú)一項(xiàng)的高度 const imageHeight = 136;
          // 獲取一列的高度 const listHeight = firstImagesList.scrollHeight;
          // 初始化列表tranlateY的高度 const startTranslateYHeight = - listHeight + imageHeight;
          // 游戲是否已經(jīng)開(kāi)始 let isStart = false;
          // 三個(gè)setTimeout的返回表示符,前兩個(gè)是第二列第三列列表開(kāi)始滾動(dòng)的延時(shí)方法,timeout3是機(jī)器停止搖晃動(dòng)畫(huà)的延時(shí)方法 let timeout1 = null; let timeout2 = null; let timeout3 = null;
          // 頁(yè)面剛進(jìn)來(lái)時(shí)列表位置初始化 initPosition(startTranslateYHeight);

          結(jié)語(yǔ)

          看到這里,小編想說(shuō)讀者你們也辛苦了,應(yīng)該費(fèi)了不少勁來(lái)看我的文章。雖然小編覺(jué)得這種需求在工作中幾乎不會(huì)遇到,但是小編覺(jué)得這是一個(gè)鍛煉自己思維能力的一個(gè)過(guò)程,如果讀者們對(duì)代碼有疑問(wèn)或建議隨時(shí)可以提出來(lái),小編會(huì)耐心解答或虛心接受。如果喜歡文章的話也給小編點(diǎn)點(diǎn)贊支持一下,碼字不易,很感謝你們的支持,最后祝大家工作順利。


          聲明:文章著作權(quán)歸作者所有,如有侵權(quán),請(qǐng)聯(lián)系小編刪除。



          最后


          • 歡迎加我微信(winty230),拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...

          • 歡迎關(guān)注「前端Q」,認(rèn)真學(xué)前端,做個(gè)專(zhuān)業(yè)的技術(shù)人...

          點(diǎn)個(gè)在看支持我吧
          瀏覽 63
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  婷婷视频| 韩国黄色毛片 | 免费一级黄 | 日本精品二区 | 久久久无码成人电影 |