初學前端用代碼實現(xiàn)一個網頁老虎機游戲
(給前端大學加星標,提升前端技能.)
作者:紅領巾小莫
https://juejin.cn/post/6890206996008534030
簡介
前兩天小編在B站看到一個AE MG動畫,動畫的內容如下:

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

老虎機的整體外觀樣式還是比較好寫的,對老虎機的外觀布局如果有興趣的話可以直接參考代碼,小編就不一一介紹了。文章主要介紹的是老虎機中間三個小格子和機身動畫的一些實現(xiàn)要點。(讀者想實操的話也可以自己找一張背景圖當作老虎機的背景,這里小編只是自己好奇而已所以就用各種基礎布局和樣式實現(xiàn)的老虎機)
格子中數(shù)字的真面目是什么?
數(shù)字列表滾動前的要點
格子中的數(shù)字列表是怎么滾動的?
“無限滾動”是怎么實現(xiàn)的?
第二個和第三個格子的延遲滾動怎么實現(xiàn)的?
隨機的滾動結果是怎么實現(xiàn)的?
游戲機是怎么抖動起來的?
重置游戲的實現(xiàn)及要注意的點
怎么獲取老虎機的游戲結果?
格子中數(shù)字的真面目是什么?
這個問題其實很簡單,格子中的內容單純只是一個ul列表,我是只是給格子添加了overflow:hidden使格子外的數(shù)字進行了隱藏。咱們先把一個格子的數(shù)字列表拿出來講,我們可以看到整個滾動過程中只有“1~6”共6個數(shù)字,我們可以先把這6個數(shù)字的列表給實現(xiàn)下。
我們利用的是ul 和 li標簽做出的列表,在布局中小編只寫了ul,雖然效果圖中只有1~6 6種情況,但是后面可能會有更多的游戲結果選項,所以li標簽就不寫死在頁面中,li標簽通過javaScript的形式添加到ul標簽中。
(這里為了方便讀者讀懂代碼我先解釋一下,小編在一開始考慮老虎機里面的內容以后可能是圖片而不是數(shù)字,所以在很多地方變量或者className的命名都名為與“images”相關。)
// 初始的選項列表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列表:
6 5 4 3 2 1
同理,第二個格子和第三個格子也可以利用相同的方式構造出相同的數(shù)字列表。通過給ul和li添加樣式之后的效果如圖:

數(shù)字列表滾動前的要點
tip: 讀者可以留意一下,firstImagesList代表的是第一個數(shù)字列表,secondsImagesList為第二個數(shù)字列表,thirdImagesList為第三個數(shù)字列表。其中代碼:
const firstImagesList = document.getElementsByClassName('images')[0];const secondsImagesList = document.getElementsByClassName('images')[1];??const?thirdImagesList?=?document.getElementsByClassName('images')[2];
我們可以從動畫中看出數(shù)字是從上往下開始滾動的。其實滾動的原理利用的是CSS3的transform:translateY()進行移動。那有人可能就有疑問了(小編你不是說從上往下滾動嗎,按照你列表這樣的布局你從上往下不是一移動就結束了嗎)。
所以我們在列表移動之前要做一件事情,我們要把這個列表的初始化布局給調整一下,將全部列表都向上移動,使數(shù)字“1”移動到格子中去。
我們先聲明一個初始化三個數(shù)字列表定位方法,其中參數(shù)startTranslateYHeight代表整個列表要向上移動的距離。
function initPosition(startTranslateYHeight) {firstImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;secondsImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;thirdImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;}
有了移動的方法之后,我們要計算出列表要向上移動的距離startTranslateYHeight。獲取向上移動距離的步驟包括:
聲明列表單獨一項也就是單個li的高度,已知li標簽的高度為136px;
獲取一列的高度,也就是整個ul標簽的高度,這個三個列表的高度都一樣大,所以我們取第一個列表利用dom的內置屬性scrollHeight獲得列表的高度;
因為整個列表向上移動到最后數(shù)字1會留在格子中,列表中全部有6個數(shù)字,我們只需要向上移動5個數(shù)字的高度即可。也就是整個整個列表的高度減去一個li標簽的高度就是我們要列表向上移動的距離。
const imageHeight = 136;const listHeight = firstImagesList.scrollHeight;??const?startTranslateYHeight?=?-?listHeight?+?imageHeight;
有了向上移動的具體距離后,我們只要執(zhí)行我們的初始化定位方法initPosition()方法即可。

格子中的數(shù)字列表是怎么滾動的?
前面我們也提到了其實滾動的原理利用的是CSS3的transform:translateY()進行移動。因為我們游戲是通過點擊手柄開始的,所以我們給手柄添加一個點擊事件,并在事件中給列表進行滾動,我們暫時默認滾動到最后一個數(shù)字,不考慮隨機結果的情況。
如果只是滾動到最后一個數(shù)字那還是比較容易的,那我們只需要將向上移動的距離還原為0就可以了,這樣子就能達到向下移動的效果。
// 點擊游戲手柄開始游戲的方法function start() {firstImagesList.style.transform = 'translateY(0)';secondsImagesList.style.transform = 'translateY(0)';thirdImagesList.style.transform = 'translateY(0)';}
我們來看一下現(xiàn)階段的效果:

是不是少了點什么對吧?沒錯,少了滾動動畫。我們只需要在游戲開始時給列表加上過渡效果即可。可能有人會問為什么要在游戲開始時再加而不是一開始寫樣式時先寫上transtion過渡。原因是這樣子阿:因為小編后續(xù)要考慮到重置游戲的問題,重置過程列表會回到最開始的定位處,如果說重置過程也有過渡樣式那是不太合理的,為了能夠保證過渡樣式是可控的小編就定義了一個添加過渡的方法,還有一個刪除過渡的方法,方便我們想要有過渡動畫就加上,不想有過渡動畫就刪除。
下面的代碼意思就是分別給每個列表添加/刪除過渡樣式類名(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() {// 游戲開始給全部數(shù)字列表添加過渡效果addTranstion();firstImagesList.style.transform = 'translateY(0)';secondsImagesList.style.transform = 'translateY(0)';thirdImagesList.style.transform = 'translateY(0)';}

到這里我們已經實現(xiàn)了數(shù)字列表的滾動效果,但是我們只是做了個簡單的從1~6的滾動,并沒有做到從頭開始的效果。簡單來說就是滾動得沒有像效果圖中那么“持久”。我們接下來就是來實現(xiàn)一下“從頭開始”,“無限滾動”的效果。
“無限滾動”是怎么實現(xiàn)的?
效果圖中我們可以看到當數(shù)字6滾動結束之后應該會重數(shù)字1開始重新滾動,話不多說我們直接揭開謎底。

可能有的小伙伴看到這里就明白小編是怎么實現(xiàn)的了。其實我這里并沒有實現(xiàn)所謂的“無限滾動”,我只是把初始化的數(shù)組按倍數(shù)給擴充了很多分,使得整個列表變得非常得長,以至于在短時間內的過渡效果中整個列表看著像是在“無限滾動”。
[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);??});
至此我們虛假的無限滾動就已經實現(xiàn)完成了(如果打滅了你們對無限滾動的期待的話請不要打小編,小編心里也苦,真正的無限滾動好像不太好寫,有感興趣的小伙伴要是知道怎么無限滾動就告訴我哈,小編也來學習學習)。
第二個和第三個格子的延遲滾動怎么實現(xiàn)的?
無限滾動介紹完之后我們來介紹一下延遲滾動的問題,我們可以看到效果圖中第二個格子是等第一個格子滾動一小會兒后才開始滾動的,第三個格子也是一樣的。
其實延遲滾動實現(xiàn)也很簡單,我們只需要給第二個數(shù)字列表和第三個數(shù)字列表各自的滾動方法中設置個定時器即可。
// 點擊游戲手柄開始游戲的方法function start() {// 游戲開始給全部數(shù)字列表添加過渡效果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)??}

隨機的滾動結果是怎么實現(xiàn)的?
隨機的滾動結果解釋起來可能會比較難以理解一點。我們再回顧一下,在上面我們實現(xiàn)向下滾動的原理是將向上移動的距離還原為0('translateY(0))來實現(xiàn)的。那試想一下如果我們還原的結果不是0,而是一個數(shù)字的高度呢?
Tip: 一個數(shù)字的高度也就是一個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、-136、-136 * 2、-136 * 3、-136 * 4、-136 * 5(單位都為px)、那么我們就可以在數(shù)字列表滾動中得到6,5,4,3,2,1共6中結果。
現(xiàn)在我們已經能夠通過改變不同的還原距離translateY()從而達到滾動結果的不同,那還有一個問題,從上面6個數(shù)中隨機出一個數(shù)來怎么做呢?滾動的結果在這里不應該是由我們人為控制的。小編想了一下,還好這里最起碼的要求是結果應該是從-136的倍數(shù)0,1,2,3,4,5種隨機出一個數(shù)來。我們通過倍數(shù)的變化就能獲取到相應的隨機值。
這里我利用了js種Math對象的Math.random()方法,Math.random()方法會返回介于 0(包含) ~ 1(不包含) 之間的一個隨機數(shù), 那如果我將Math.random的結果乘以6,那我不就得到0~6(不包含6)之間的一個隨機數(shù),并且我將獲取的隨機數(shù)通過Math.floor()做一個向下取整,那我不就得到0,1,2,3,4,5的隨機數(shù)了。
目前整個游戲的開始方法整理如下:
// 初始的選項列表initImagesArr中有6個值,也就是單列數(shù)據(jù)列表總的情況會有6種中間內容省略。。。
// 單個數(shù)字的高度,即單個li標簽的高度const imageHeight = 136;// resultNum為滾動結果情況的個數(shù),這里有6個數(shù)字,也就是有6種情況// translateY(${-imageHeight * radom1}px)為列表1最終還原的位置,以此類推function start(resultNum) {// 游戲開始給全部數(shù)字列表添加過渡效果addTranstion();// 每個列表滾動的隨機結果0~resultNumlet 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為單個數(shù)字列表滾動結果的全部可能性。另外因為全部結果的情況總數(shù)由初始數(shù)字列表initImagesArr: [1,2,3,4,5,6]中的數(shù)字個數(shù)所決定的,所以只需要將initImagesArr.length作為參數(shù)傳給start()方法即可。
注意點2:三個數(shù)字列表的隨機結果都不同,其中radom1為列表1的隨機結果,radom2為列表2的隨機結果,radom3為列表3的隨機結果。Math.random()*resultNum的值為 0 * resultNum ~ 1 * resultNum(不包含1 * resultNum)。本例中resultNum為6,則結果為0 ~ 5.999999999 經過Math.floor()向下取整過后的結果為0 ~ 5。
經過以上的處理之后,我們的隨機結果就已經成功實現(xiàn)了。
游戲機是怎么抖動起來的?
前面可能會復雜一點,這里就我們聊個稍微簡單易懂的東西。從效果圖中我們可以看出老虎機從開始游戲到游戲快結束時一直是在抖動的,關于這個我也給大家稍微分享一下怎么實現(xiàn)的。

其實就一個東西,加個動畫。這里我先直接貼上代碼:
.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%)}的形式實現(xiàn)老虎機相對可視區(qū)域水平垂直居中對齊的效果。
// 老虎機相對可視區(qū)域水平垂直居中對齊的效果。.machine {position: absolute;top: 50%;left: 50%;transform: translate(-50%,-50%);}
解釋一下代碼,小編通過給整個老虎機從各個方向都移動一下,并且以很快的速度完成(這里用的是0.1s完成的動畫),動畫的循環(huán)次數(shù)為無限次infinite,從而實現(xiàn)了老虎機一直在抖的效果。
不過要稍微留意一點,我們這里抖動應該也是要可控的,因為老虎機在游戲快結束時會停止抖動。跟我們之前做過渡效果可控的方式一樣,我們也給動畫聲明一個添加抖動和移除抖動的方法:
// 給老虎機添加抖動效果function startShake() {document.getElementsByClassName('machine')[0].classList.add('shake');}// 移除老虎機抖動效果function stopShake() {document.getElementsByClassName('machine')[0].classList.remove('shake');??}
之后我們在游戲開始的時候調用startShake(),然后在游戲快結束時調用stopShake()。不過在下面代碼我們可以看到移除抖動效果是在2.6s之后才執(zhí)行的,原因是第三個數(shù)字列表需要等到游戲開始1s才開始滾動,而且滾動的過渡時間為2s,那等到第三個數(shù)字列表滾動到結束總共需要3s,這里小編想要在第三個數(shù)字列表滾動結束之前將老虎機停止抖動,所以將移除抖動方法在游戲開始2.6s之后才執(zhí)行。
// 單個數(shù)字的高度,即單個li標簽的高度const imageHeight = 136;function start(resultNum) {// 游戲開始給全部數(shù)字列表添加過渡效果addTranstion();// 游戲開始給老虎機添加抖動效果startShake();// 每個列表滾動的隨機結果0~resultNumlet 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)}
重置游戲的實現(xiàn)及要注意的點
老虎機從開始游戲到結束游戲的整個環(huán)節(jié)我們都已經實現(xiàn)完成了。但是呢,小編還想再玩一把,然后我在第二次點擊開始手柄之前就想到了這個老虎機存在的缺陷,總結起來包括以下幾點:
游戲從開始到結束之后,再次點擊開始手柄,應該將游戲進行重置
重置游戲的過程中不應該出現(xiàn)過渡效果
重置游戲的過程中機器不應該繼續(xù)在搖晃
如果將游戲進行重置了,第一次開始游戲方法中的定時器方法應該清空。
小編給這臺老虎機做了個設定,當游戲手柄點擊第一下時,游戲開始。當游戲手柄點擊第二下時,游戲要結束并重置游戲。當游戲手柄點擊第三下時,游戲又再度重新開始。
這里可能有個疑惑就是開始游戲手柄的點擊事件只綁定了一個開始游戲start()的方法,那怎么判斷游戲是開始還是重置?
中間內容省略。。。這里小編給start寫了個flag,這個當flag為true時,會執(zhí)行開始游戲的方法,當flag為false時,會執(zhí)行重置游戲的方法,每次點擊時將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 {// 重置游戲,這里實現(xiàn)重置游戲的方法}??}
那重置游戲需要我們做什么呢?
1、首先當然是將所有的數(shù)字列表回到初始位置。我們在前面“數(shù)字列表滾動前的要點”中已經實現(xiàn)了初始化定位方法initPosition()方法,在上面else代碼塊中我們只要調用initPosition(startTranslateYHeight)即可將全部數(shù)字列表回到初始的位置。Tip:在前面“數(shù)字列表滾動前的要點”中我們已經介紹了startTranslateYHeight為列表向上移動的距離。
function initPosition(startTranslateYHeight) {firstImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;secondsImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;thirdImagesList.style.transform = `translateY(${startTranslateYHeight}px)`;??}
2、 重置游戲時數(shù)字列表在回到初始位置的過程中,由于游戲開始過程中數(shù)字列表添加了過渡動畫,會導致數(shù)字列表在回到初始位置的過程也會存在過渡動畫,因此我們需要調用之前先聲明好的removeTranstion()來刪除全部數(shù)字列表的過渡效果。

3、 重置游戲時由于第一次開始游戲過程中給老虎機添加了抖動效果,重置時應該移除抖動效果。在else代碼塊中調用stopShake()即可。
4、 假如在開始游戲后在很短的時間內又點擊了重置游戲,這時候開始游戲中未執(zhí)行的定時器中的方法應該通過clearTimeout()給予一一清除,否則在重置游戲時還是會執(zhí)行開始游戲中的方法。

那么else代碼塊中的代碼應該為:
else {clearTimeout(timeout1);clearTimeout(timeout2);clearTimeout(timeout3);// 停止抖動stopShake();// 重置時因為列表會重新移動到初始位置,所以要清除掉過渡效果removeTranstion();// 各個列表回到最初的位置initPosition(startTranslateYHeight);}
怎么獲取老虎機的游戲結果
是個游戲總會有個結果的。這里我們實現(xiàn)在老虎機游戲結束之后,將游戲結果給打印出來。由于我們的結果是通過radom1,radom2,radom3隨機結果得出,當radom1為0時結果為6,random1為1時結果為5,依次類推。我們可以得到最終的結果應該為 initImagesArr[radom1], initImagesArr[radom2], initImagesArr[radom3]。
前面我們也有提到游戲結束的整個過程需要經歷3s,所以我們定義一個定時器在開始游戲3s后執(zhí)行并打印出游戲結果即可。
代碼附錄
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:
{width: 484px;height: 513px;position: absolute;top: 50%;left: 50%;transform: translate(-50%,-50%);display: flex;{width: 400px;: #c8d0e3;height: 513px;border: 14px solid #2e2a27;: 34px;: border-box;{width: 278px;border: 16px solid #2e2a27;: 16px;margin: 54px auto 0;display: flex;{width: 82px;height: 136px;overflow: hidden;{: none;margin: 0;padding: 0;border: none;li {margin: 0;padding: 0;border: 0;: center;height: 136px;: 136px;: 30px;: wheat;}}{transition: all ease 2s;}}}:nth-child(1) {: 16px solid #2e2a27;}:nth-child(2) {: 16px solid #2e2a27;}{width: 240px;margin: 20px auto 0;img {width: 100%;height: auto;}}}{width: 34px;height: 130px;: #b1b8d4;border: 14px solid #2e2a27;: none;: border-box;: 24px;: 24px;transform: translateY(250px);position: relative;{width: 64px;position: absolute;left: 30px;bottom: 28px;cursor: pointer;{width: 34px;height: 34px;: #ff6169;: 50%;border: 15px solid #2e2a27;transform: translateY(2px);}{width: 16px;height: 106px;margin: 0 auto;: #2e2a27;transform: translateY(1px);}{width: 56px;height: 48px;border: 15px solid #2e2a27;: none;: 25px;: 25px;: #c8d0e3;: border-box;margin: 0 auto;}}}}{animation: shake 0.1s infinite;}shake {{left: 49.7%;}{top: 49.7%;}{left: 50%;}{top: 50%;}}
javaScript:
// 以下的images命名可能是考慮老虎機里的內容可能會被替換成水果等其他圖片而非數(shù)字,所以將相關的內容變量命名為與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列表的每一項的高度// 列表數(shù)組的長度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 {// 取消上一次未執(zhí)行完的方法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);})// 獲取第一個列表的domconst 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);
結語
看到這里,小編想說讀者你們也辛苦了,應該費了不少勁來看我的文章。雖然小編覺得這種需求在工作中幾乎不會遇到,但是小編覺得這是一個鍛煉自己思維能力的一個過程,如果讀者們對代碼有疑問或建議隨時可以提出來,小編會耐心解答或虛心接受。如果喜歡文章的話也給小編點點贊支持一下,碼字不易,很感謝你們的支持,最后祝大家工作順利。

