超有趣,初學前端用代碼實現一個網頁遊戲老虎機遊戲

簡介

前兩天小編在B站看到一個AE MG動畫,動畫的內容以下:javascript

這個動畫仍是挺有意思的,可是小編第一個反應這要是哪一天某位ui姐姐或產品姐姐給小編提了這樣子的需求,那小編豈不是當場要自閉?我本着本身的好奇心,實現了一個簡易版的老虎機:css

老虎機的總體外觀樣式仍是比較好寫的,對老虎機的外觀佈局若是有興趣的話能夠直接參考代碼,小編就不一一介紹了。文章主要介紹的是老虎機中間三個小格子和機身動畫的一些實現要點。(讀者想實操的話也能夠本身找一張背景圖看成老虎機的背景,這裏小編只是本身好奇而已因此就用各類基礎佈局和樣式實現的老虎機)html

  • 格子中數字的真面目是什麼?前端

  • 數字列表滾動前的要點java

  • 格子中的數字列表是怎麼滾動的?數組

  • 「無限滾動」是怎麼實現的?app

  • 第二個和第三個格子的延遲滾動怎麼實現的?dom

  • 隨機的滾動結果是怎麼實現的?佈局

  • 遊戲機是怎麼抖動起來的?學習

  • 重置遊戲的實現及要注意的點

  • 怎麼獲取老虎機的遊戲結果?

格子中數字的真面目是什麼?

這個問題其實很簡單,格子中的內容單純只是一個ul列表,我是隻是給格子添加了overflow:hidden使格子外的數字進行了隱藏。我們先把一個格子的數字列表拿出來說,咱們能夠看到整個滾動過程當中只有「1~6」共6個數字,咱們能夠先把這6個數字的列表給實現下。

咱們利用的是ul 和 li標籤作出的列表,在佈局中小編只寫了ul,雖然效果圖中只有1~6 6種狀況,可是後面可能會有更多的遊戲結果選項,因此li標籤就不寫死在頁面中,li標籤經過javaScript的形式添加到ul標籤中。

(這裏爲了方便讀者讀懂代碼我先解釋一下,小編在一開始考慮老虎機裏面的內容之後多是圖片而不是數字,因此在不少地方變量或者className的命名都名爲與「images」相關。)

<ul class="images"><ul/>
// 初始的選項列表
  let initImagesArr = [6, 5, 4, 3, 2, 1];
  // 獲取第一個ul列表
  const firstImagesList = document.getElementsByClassName('images')[0];
   // 構造列表li添加到ul標籤中去
  initImagesArr.forEach(item => {
    const li = document.createElement('li');
    li.innerHTML = item;
    firstImagesList.appendChild(li);
  });

這樣子咱們就能夠得到這樣的一個ul列表:

<ul class="images">
      <li>6</li>
      <li>5</li>
      <li>4</li>
      <li>3</li>
      <li>2</li>
      <li>1</li>
  <ul/>

同理,第二個格子和第三個格子也能夠利用相同的方式構造出相同的數字列表。經過給ul和li添加樣式以後的效果如圖:

數字列表滾動前的要點

tip: 讀者能夠留意一下,firstImagesList表明的是第一個數字列表,secondsImagesList爲第二個數字列表,thirdImagesList爲第三個數字列表。其中代碼:

const firstImagesList = document.getElementsByClassName('images')[0];
  const secondsImagesList = document.getElementsByClassName('images')[1];
  const thirdImagesList = document.getElementsByClassName('images')[2];

咱們能夠從動畫中看出數字是從上往下開始滾動的。其實滾動的原理利用的是CSS3的transform:translateY()進行移動。那有人可能就有疑問了(小編你不是說從上往下滾動嗎,按照你列表這樣的佈局你從上往下不是一移動就結束了嗎)。

因此咱們在列表移動以前要作一件事情,咱們要把這個列表的初始化佈局給調整一下,將所有列表都向上移動,使數字「1」移動到格子中去。

咱們先聲明一個初始化三個數字列表定位方法,其中參數startTranslateYHeight表明整個列表要向上移動的距離。

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

有了移動的方法以後,咱們要計算出列表要向上移動的距離startTranslateYHeight。獲取向上移動距離的步驟包括:

  1. 聲明列表單獨一項也就是單個li的高度,已知li標籤的高度爲136px;

  2. 獲取一列的高度,也就是整個ul標籤的高度,這個三個列表的高度都同樣大,因此咱們取第一個列表利用dom的內置屬性scrollHeight得到列表的高度;

  3. 由於整個列表向上移動到最後數字1會留在格子中,列表中所有有6個數字,咱們只須要向上移動5個數字的高度便可。也就是整個整個列表的高度減去一個li標籤的高度就是咱們要列表向上移動的距離。

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

格子中的數字列表是怎麼滾動的?

前面咱們也提到了其實滾動的原理利用的是CSS3的transform:translateY()進行移動。由於咱們遊戲是經過點擊手柄開始的,因此咱們給手柄添加一個點擊事件,並在事件中給列表進行滾動,咱們暫時默認滾動到最後一個數字,不考慮隨機結果的狀況。

若是隻是滾動到最後一個數字那仍是比較容易的,那咱們只須要將向上移動的距離還原爲0就能夠了,這樣子就能達到向下移動的效果。

// 點擊遊戲手柄開始遊戲的方法
function start() {
  firstImagesList.style.transform = 'translateY(0)';
  secondsImagesList.style.transform = 'translateY(0)';
  thirdImagesList.style.transform = 'translateY(0)';
}

咱們來看一下現階段的效果:

是否是少了點什麼對吧?沒錯,少了滾動動畫。咱們只須要在遊戲開始時給列表加上過渡效果便可。可能有人會問爲何要在遊戲開始時再加而不是一開始寫樣式時先寫上transtion過渡。緣由是這樣子阿:由於小編後續要考慮到重置遊戲的問題,重置過程列表會回到最開始的定位處,若是說重置過程也有過渡樣式那是不太合理的,爲了可以保證過渡樣式是可控的小編就定義了一個添加過渡的方法,還有一個刪除過渡的方法,方便咱們想要有過渡動畫就加上,不想有過渡動畫就刪除。

下面的代碼意思就是分別給每一個列表添加/刪除過渡樣式類名(className),刪除過渡咱們會在重置動畫中使用到。

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

而後咱們只須要只開始遊戲方法中調用添加過渡方法便可:

function start() {
  // 遊戲開始給所有數字列表添加過渡效果
  addTranstion();
  firstImagesList.style.transform = 'translateY(0)';
  secondsImagesList.style.transform = 'translateY(0)';
  thirdImagesList.style.transform = 'translateY(0)';
}

到這裏咱們已經實現了數字列表的滾動效果,可是咱們只是作了個簡單的從1~6的滾動,並無作到從頭開始的效果。簡單來講就是滾動得沒有像效果圖中那麼「持久」。咱們接下來就是來實現一下「從頭開始」,「無限滾動」的效果。

「無限滾動」是怎麼實現的?

效果圖中咱們能夠看到當數字6滾動結束以後應該會重數字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,6,5,4,3,2,1]

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

那麼此時前面添加li標籤的代碼就得修改一下了,將initImageArr修改成新的選項列表imagesArr

// 構造列表li添加到ul標籤中去
  imagesArr.forEach(item => {
    const li = document.createElement('li');
    li.innerHTML = item;
    firstImagesList.appendChild(li);
  });

至此咱們虛假的無限滾動就已經實現完成了(若是打滅了大家對無限滾動的期待的話請不要打小編,小編內心也苦,真正的無限滾動好像不太好寫,有感興趣的小夥伴要是知道怎麼無限滾動就告訴我哈,小編也來學習學習)。

第二個和第三個格子的延遲滾動怎麼實現的?

無限滾動介紹完以後咱們來介紹一下延遲滾動的問題,咱們能夠看到效果圖中第二個格子是等第一個格子滾動一小會兒後纔開始滾動的,第三個格子也是同樣的。

其實延遲滾動實現也很簡單,咱們只須要給第二個數字列表和第三個數字列表各自的滾動方法中設置個定時器便可。

// 點擊遊戲手柄開始遊戲的方法
  function start() {
    // 遊戲開始給所有數字列表添加過渡效果
    addTranstion();
    firstImagesList.style.transform = 'translateY(0)';
    // 列表2延遲0.5s後滾動
    timeout1 = setTimeout(() => {
      secondsImagesList.style.transform = 'translateY(0)';
    },500)
    // 列表3延遲1s後滾動
    timeout2 = setTimeout(() => {
      thirdImagesList.style.transform = 'translateY(0)';
    },1000)
  }

隨機的滾動結果是怎麼實現的?

隨機的滾動結果解釋起來可能會比較難以理解一點。咱們再回顧一下,在上面咱們實現向下滾動的原理是將向上移動的距離還原爲0('translateY(0))來實現的。那試想一下若是咱們還原的結果不是0,而是一個數字的高度呢?

Tip: 一個數字的高度也就是一個li標籤的高度,前面咱們已知一個li標籤高度是136px

咱們來改寫代碼試試:

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

效果以下:

咱們能夠看到,若是咱們將定位只還原到translateY(-136px),那滾動的結果會是5。以此類推,若是咱們只還原到0、-13六、-136 * 二、-136 * 三、-136 * 四、-136 * 5(單位都爲px)、那麼咱們就能夠在數字列表滾動中獲得6,5,4,3,2,1共6中結果。

如今咱們已經可以經過改變不一樣的還原距離translateY()從而達到滾動結果的不一樣,那還有一個問題,從上面6個數中隨機出一個數來怎麼作呢?滾動的結果在這裏不該該是由咱們人爲控制的。小編想了一下,還好這裏最起碼的要求是結果應該是從-136的倍數0,1,2,3,4,5種隨機出一個數來。咱們經過倍數的變化就能獲取到相應的隨機值。

這裏我利用了js種Math對象的Math.random()方法,Math.random()方法會返回介於 0(包含) ~ 1(不包含) 之間的一個隨機數, 那若是我將Math.random的結果乘以6,那我不就獲得0~6(不包含6)之間的一個隨機數,而且我將獲取的隨機數經過Math.floor()作一個向下取整,那我不就獲得0,1,2,3,4,5的隨機數了。

目前整個遊戲的開始方法整理以下:

//  初始的選項列表initImagesArr中有6個值,也就是單列數據列表總的狀況會有6種
<div class="handle" onclick="start(initImagesArr.length)">
  中間內容省略。。。
</div>
// 單個數字的高度,即單個li標籤的高度
 const imageHeight = 136;
 // resultNum爲滾動結果狀況的個數,這裏有6個數字,也就是有6種狀況
 // translateY(${-imageHeight * radom1}px)爲列表1最終還原的位置,以此類推
 function start(resultNum) {
  // 遊戲開始給所有數字列表添加過渡效果
  addTranstion();
  // 每一個列表滾動的隨機結果0~resultNum
  let radom1 = Math.floor(Math.random()*resultNum);
  let radom2 = Math.floor(Math.random()*resultNum);
  let radom3 = Math.floor(Math.random()*resultNum);
  // 列表1開始滾動
  firstImagesList.style.transform = `translateY(${-imageHeight * radom1}px)`;
  // 列表2延遲0.5s後滾動
  timeout1 = setTimeout(() => {
    secondsImagesList.style.transform = `translateY(${-imageHeight * radom2}px)`;
  },500)
  // 列表3延遲1s後滾動
  timeout2 = setTimeout(() => {
    thirdImagesList.style.transform = `translateY(${-imageHeight * radom3}px)`;
  },1000)
}

注意點1:方法中resultNum爲單個數字列表滾動結果的所有可能性。另外由於所有結果的狀況總數由初始數字列表initImagesArr: [1,2,3,4,5,6]中的數字個數所決定的,因此只須要將initImagesArr.length做爲參數傳給start()方法便可。

注意點2:三個數字列表的隨機結果都不一樣,其中radom1爲列表1的隨機結果,radom2爲列表2的隨機結果,radom3爲列表3的隨機結果。Math.random()*resultNum的值爲 0 * resultNum ~ 1 * resultNum(不包含1 * resultNum)。本例中resultNum爲6,則結果爲0 ~ 5.999999999 通過Math.floor()向下取整事後的結果爲0 ~ 5。

通過以上的處理以後,咱們的隨機結果就已經成功實現了。

遊戲機是怎麼抖動起來的?

前面可能會複雜一點,這裏就咱們聊個稍微簡單易懂的東西。從效果圖中咱們能夠看出老虎機從開始遊戲到遊戲快結束時一直是在抖動的,關於這個我也給你們稍微分享一下怎麼實現的。

其實就一個東西,加個動畫。這裏我先直接貼上代碼:

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

在寫樣式時小編經過相對定位的形式position:absoulte 配合{left:50%;top:50%;translate(-50%,-50%)}的形式實現老虎機相對可視區域水平垂直居中對齊的效果。

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

解釋一下代碼,小編經過給整個老虎機從各個方向都移動一下,而且以很快的速度完成(這裏用的是0.1s完成的動畫),動畫的循環次數爲無限次infinite,從而實現了老虎機一直在抖的效果。

不過要稍微留意一點,咱們這裏抖動應該也是要可控的,由於老虎機在遊戲快結束時會中止抖動。跟咱們以前作過渡效果可控的方式同樣,咱們也給動畫聲明一個添加抖動和移除抖動的方法:

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

以後咱們在遊戲開始的時候調用startShake(),而後在遊戲快結束時調用stopShake()。不過在下面代碼咱們能夠看到移除抖動效果是在2.6s以後才執行的,緣由是第三個數字列表須要等到遊戲開始1s纔開始滾動,並且滾動的過渡時間爲2s,那等到第三個數字列表滾動到結束總共須要3s,這裏小編想要在第三個數字列表滾動結束以前將老虎機中止抖動,因此將移除抖動方法在遊戲開始2.6s以後才執行。

// 單個數字的高度,即單個li標籤的高度
 const imageHeight = 136;
 function start(resultNum) {
  // 遊戲開始給所有數字列表添加過渡效果
  addTranstion();
  // 遊戲開始給老虎機添加抖動效果
  startShake();
  // 每一個列表滾動的隨機結果0~resultNum
  let radom1 = Math.floor(Math.random()*resultNum);
  let radom2 = Math.floor(Math.random()*resultNum);
  let radom3 = Math.floor(Math.random()*resultNum);
  // 列表1開始滾動
  firstImagesList.style.transform = `translateY(${-imageHeight * radom1}px)`;
  // 列表2延遲0.5s後滾動
  timeout1 = setTimeout(() => {
    secondsImagesList.style.transform = `translateY(${-imageHeight * radom2}px)`;
  },500)
  // 列表3延遲1s後滾動
  timeout2 = setTimeout(() => {
    thirdImagesList.style.transform = `translateY(${-imageHeight * radom3}px)`;
  },1000)
  // 老虎機在延遲2.6妙後移除抖動效果
  timeout3 = setTimeout(() => {
    stopShake();
  },2600)
}

重置遊戲的實現及要注意的點

老虎機從開始遊戲到結束遊戲的整個環節咱們都已經實現完成了。可是呢,小編還想再玩一把,而後我在第二次點擊開始手柄以前就想到了這個老虎機存在的缺陷,總結起來包括如下幾點:

  • 遊戲從開始到結束以後,再次點擊開始手柄,應該將遊戲進行重置

  • 重置遊戲的過程當中不該該出現過渡效果

  • 重置遊戲的過程當中機器不該該繼續在搖晃

  • 若是將遊戲進行重置了,第一次開始遊戲方法中的定時器方法應該清空。

小編給這臺老虎機作了個設定,當遊戲手柄點擊第一下時,遊戲開始。當遊戲手柄點擊第二下時,遊戲要結束並重置遊戲。當遊戲手柄點擊第三下時,遊戲又再度從新開始。

這裏可能有個疑惑就是開始遊戲手柄的點擊事件只綁定了一個開始遊戲start()的方法,那怎麼判斷遊戲是開始仍是重置?

<div class="handle" onclick="start(initImagesArr.length)">
  中間內容省略。。。
</div>

這裏小編給start寫了個flag,這個當flag爲true時,會執行開始遊戲的方法,當flag爲false時,會執行重置遊戲的方法,每次點擊時將flag的值從新賦爲flag的反向值便可。

// 遊戲是否開始 true爲開始遊戲,false爲重置遊戲
  let isStart = false;
  function start(imagesArrLength) {
    // flag取反
    isStart = !isStart;
    // 開始遊戲
    if (isStart) {
      // 開始遊戲就給列表加過渡效果
      addTranstion();
      startShake();
 
 
      // 每一個列表滾動的隨機結果
      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後滾動
      timeout1 = setTimeout(() => {
        secondsImagesList.style.transform = `translateY(${-imageHeight * radom2}px)`;
      },500)
 
 
      // 列表3延遲1s後滾動
      timeout2 = setTimeout(() => {
        thirdImagesList.style.transform = `translateY(${-imageHeight * radom3}px)`;
      },1000)
 
 
      timeout3 = setTimeout(() => {
        stopShake();
      },2600)
    }else {
      // 重置遊戲,這裏實現重置遊戲的方法
    }
  }

那重置遊戲須要咱們作什麼呢?

一、首先固然是將全部的數字列表回到初始位置。咱們在前面「數字列表滾動前的要點」中已經實現了初始化定位方法initPosition()方法,在上面else代碼塊中咱們只要調用initPosition(startTranslateYHeight)便可將所有數字列表回到初始的位置。Tip:在前面「數字列表滾動前的要點」中咱們已經介紹了startTranslateYHeight爲列表向上移動的距離。

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

二、 重置遊戲時數字列表在回到初始位置的過程當中,因爲遊戲開始過程當中數字列表添加了過渡動畫,會致使數字列表在回到初始位置的過程也會存在過渡動畫,所以咱們須要調用以前先聲明好的removeTranstion()來刪除所有數字列表的過渡效果。

該圖片來源:網頁遊戲http://www.hp91.cn/ 頁遊

三、 重置遊戲時因爲第一次開始遊戲過程當中給老虎機添加了抖動效果,重置時應該移除抖動效果。在else代碼塊中調用stopShake()便可。

四、 假如在開始遊戲後在很短的時間內又點擊了重置遊戲,這時候開始遊戲中未執行的定時器中的方法應該經過clearTimeout()給予一一清除,不然在重置遊戲時仍是會執行開始遊戲中的方法。

該圖片來源:網頁遊戲http://www.hp91.cn/ 頁遊

那麼else代碼塊中的代碼應該爲:

else {
  clearTimeout(timeout1);
  clearTimeout(timeout2);
  clearTimeout(timeout3);
  // 中止抖動
  stopShake();
  // 重置時由於列表會從新移動到初始位置,因此要清除掉過渡效果
  removeTranstion();
  // 各個列表回到最初的位置
  initPosition(startTranslateYHeight);
}

怎麼獲取老虎機的遊戲結果

是個遊戲總會有個結果的。這裏咱們實如今老虎機遊戲結束以後,將遊戲結果給打印出來。因爲咱們的結果是經過radom1,radom2,radom3隨機結果得出,當radom1爲0時結果爲6,random1爲1時結果爲5,依次類推。咱們能夠獲得最終的結果應該爲 initImagesArr[radom1], initImagesArr[radom2], initImagesArr[radom3]。

前面咱們也有提到遊戲結束的整個過程須要經歷3s,因此咱們定義一個定時器在開始遊戲3s後執行並打印出遊戲結果便可。

代碼附錄

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命名多是考慮老虎機裏的內容可能會被替換成水果等其餘圖片而非數字,因此將相關的內容變量命名爲與images相關
  // 頁面剛加載時三個列表的初始定位
  function initPosition(startTranslateYHeight) {
    firstImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;
    secondsImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;
    thirdImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;
  }
 
 
  // 所有列表添加過渡效果
  function addTranstion() {
    firstImagesList.classList.add('transtion');
    secondsImagesList.classList.add('transtion');
    thirdImagesList.classList.add('transtion');
  }
 
 
  // 所有列表刪除過渡效果
  function removeTranstion(imagesList) {
    firstImagesList.classList.remove('transtion');
    secondsImagesList.classList.remove('transtion');
    thirdImagesList.classList.remove('transtion');
  }
 
 
  // 給老虎機添加搖晃動畫
  function startShake() {
    document.getElementsByClassName('machine')[0].classList.add('shake');
  }
  // 中止老虎機搖晃動畫
  function stopShake() {
    document.getElementsByClassName('machine')[0].classList.remove('shake');
  }
 
 
  // 點擊第一個是開始,點擊第二次是充值遊戲
  // startTranslateYHeight 列表的初始化時translateY的距離
  // imageHeight列表的每一項的高度
  // 列表數組的長度
  function start(imagesArrLength) {
    isStart = !isStart;
    // 開始遊戲
    if (isStart) {
      // 開始遊戲就給列表加過渡效果
      addTranstion();
      startShake();
 
 
      // 每一個列表滾動的隨機結果
      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後滾動
      timeout1 = setTimeout(() => {
        secondsImagesList.style.transform = `translateY(${-imageHeight * radom2}px)`;
      },500)
 
 
      // 列表3延遲1s後滾動
      timeout2 = setTimeout(() => {
        thirdImagesList.style.transform = `translateY(${-imageHeight * radom3}px)`;
      },1000)
      // 延遲2.6秒後中止抖動
      timeout3 = setTimeout(() => {
        stopShake();
      },2600)
      // 遊戲結束後打印結果
      timeout4 = setTimeout(() => {
        console.log(initImagesArr[radom1],initImagesArr[radom2],initImagesArr[radom3]);
      },3000)
      // 重置遊戲
    }else {
      // 取消上一次未執行完的方法
      clearTimeout(timeout1);
      clearTimeout(timeout2);
      clearTimeout(timeout3);
      clearTimeout(timeout4);
      stopShake();
      // 重置時由於列表會從新移動到初始位置,因此要清除掉過渡效果
      removeTranstion();
      // 各個列表回到最初的位置
      initPosition(startTranslateYHeight);
    }
  }
 
 
  // 初始的選項列表
  let initImagesArr = [6, 5, 4, 3, 2, 1];
  let imagesArr = [6, 5, 4, 3, 2, 1];
  // 加長整個選項列表,以完成一個虛假的滾動的效果
  new Array(20).fill('').forEach(() => {
    imagesArr = imagesArr.concat(...initImagesArr);
  })
 
 
  // 獲取第一個列表的dom
  const firstImagesList = document.getElementsByClassName('images')[0];
  const secondsImagesList = document.getElementsByClassName('images')[1];
  const thirdImagesList = document.getElementsByClassName('images')[2];
  // 構造列表li添加到ul標籤中去
  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);
  });
 
 
 
 
  // 列表單獨一項的高度
  const imageHeight = 136;
 
 
  // 獲取一列的高度
  const listHeight = firstImagesList.scrollHeight;
 
 
  // 初始化列表tranlateY的高度
  const startTranslateYHeight = - listHeight + imageHeight;
 
 
  // 遊戲是否已經開始
  let isStart = false;
 
 
  // 三個setTimeout的返回表示符,前兩個是第二列第三列列表開始滾動的延時方法,timeout3是機器中止搖晃動畫的延時方法
  let timeout1 = null;
  let timeout2 = null;
  let timeout3 = null;
 
 
  // 頁面剛進來時列表位置初始化
  initPosition(startTranslateYHeight);

結語

看到這裏,小編想說讀者大家也辛苦了,應該費了很多勁來看個人文章超有趣,初學前端用代碼實現一個頁遊老虎機遊戲。雖然小編以爲這種需求在工做中幾乎不會遇到,可是小編以爲這是一個鍛鍊本身思惟能力的一個過程,若是讀者們對代碼有疑問或建議隨時能夠提出來,小編會耐心解答或虛心接受。若是喜歡文章的話也給小編點點贊支持一下,碼字不易,很感謝大家的支持,最後祝你們工做順利

相關文章
相關標籤/搜索