Vue<幸運抽獎-老虎機??>

2021-01-28 11-47-11.gif

在線演示地址

沒用canvas!沒用canvas!沒用canvas!你沒聽錯,這里只要你css+html學的好,簡單的 animation 動畫,教你做老虎機!
下面我將分幾個步驟,告訴大家如何實現的,想直接看代碼的,可以直接看最下面的代碼
此篇文章實現的原理和 Vue<幸運抽獎-大轉盤> 比較類似,所以像更好的理解老虎機的實現原理,可以去參考下,這里針對細節方面就不做過多的解釋了。

實現步驟:

1.布局實現
用到了 perspective和 transform-style: preserve-3d;旋轉,或許直接看圖就能明白個大概了,這樣就能很好的實現根據獎品個數旋轉成一個圓形,那么外層只需要一個標簽包裹,設置一個 overflow: hidden 就???~

:style="`transform: rotateX(${360/list.length*index}deg)  translateZ(${13.6*list.length}px)`"

image.png

2.動畫轉動
如:7個獎品,那么每個獎品都會有針對自己的class動畫,及.wr1,.wr2,.wr3...,如果抽中的結果獎品是2,那么相當于每個列表的獎品都會去執行 .wr2的動畫。

winner: 2, //指定的獎品 null時為不中獎
2021-01-28 12-11-55.gif

詳細的實現原理可參考文章 Vue<幸運抽獎-大轉盤> 實現步驟** 3** ???介紹

這里會遇到一個問題,就是如果在連續抽獎時,下次抽獎開始的時候,還想接著當前的位置繼續抽獎,該怎么搞?
這里提供了一個animationClass用來儲存每次抽獎后的位置和class,方便下次轉動時的動畫,根據當前的位置進行轉動,以及移除相應class添加新的class動畫

 animationClass: [] //3個抽獎模塊對應的動畫屬性,方便后來對應添加和移除該class樣式

因為重置的位置在 360deg以內 所以不會影響css動畫中5 * 360deg的起步值,因此每次還是會繼續旋轉起來

this.animationClass[index] = {
          num: winner,
          class: `wr${winner}`,
          rotate: (360 / this.list.length) * -winner
        }

使用參數:

list: [],//獎品列表,目前最多支持8個,想支持更多,可以手動添加對應的css樣式動畫
winner: null, //指定的獎品 null時為不中獎
loading: false, //抽獎執行狀態,防止用戶多次點擊
gundongElement: null,//儲存獲取到的滾動容器的標簽
animationClass: [] //3個抽獎模塊對應的動畫屬性,方便后來對應添加和移除該class樣式
$time:3s; //老虎機的轉動時間 3s 后停止,修改后需要在計時器 setTimeout 中修改對應獲獎提示的時間

代碼如下:

<template>
  <div class="overall">
    <div>
      <div class="cj-box">
        <div class="cj-block" v-for="item in 3" :key="item">
          <ul class="gundong">
            <li
              v-for="(i,index) in list"
              :key="index"
              :style="`transform: rotateX(${360/list.length*index}deg)  translateZ(${13.6*list.length}px)`"
            >{{i.title}}</li>
          </ul>
        </div>
      </div>
      <div class="chou" @click="start">抽獎</div>
    </div>
  </div>
</template>

<script>
export default {
  computed: {},
  data() {
    return {
      list: [
        {
          title: '0'
        },
        {
          title: '1'
        },
        {
          title: '2'
        },
        {
          title: '3'
        },
        {
          title: '4'
        },
        {
          title: '5'
        },
        {
          title: '6'
        },
        {
          title: '7'
        }
      ], //獎品列表,目前最多支持7個,想支持更多,可以手動添加對應的css樣式動畫
      winner: null, //指定的獎品 null時為不中獎
      loading: false, //抽獎執行狀態,防止用戶多次點擊
      gundongElement: null, //儲存獲取到的滾動容器的標簽
      animationClass: [] //3個抽獎模塊對應的動畫屬性,方便后來對應添加和移除該class樣式
    }
  },
  mounted() {
    //通過獲取獎品個數,來改變css樣式中每個獎品動畫的旋轉角度
    // var(--nums) 實現css動畫根據獎品個數,動態改變
    let root = document.querySelector(':root')
    root.style.setProperty('--nums', this.list.length)
  },
  methods: {
    //開始抽獎
    start() {
      if (!this.loading) {
        this.gundongElement = document.querySelectorAll('.gundong')
        this.gundongElement.forEach((element, index) => {
          if (this.animationClass[index]) {
            element.classList.remove(this.animationClass[index].class)
          }
        })
        //此處可指定后端返回的指定獎品------------------------------------------------
        // this.winner = this.winner
        this.winCallback()
        this.loading = true
      }
    },
    //中獎返回方法
    winCallback() {
      setTimeout(() => {
        /* 此處是為了解決當下次抽中的獎勵與這次相同,動畫不重新執行的 */
        /* 添加一個定時器,是為了解決動畫屬性的替換效果,實現動畫的重新執行 */
        this.gundongElement.forEach((element, index) => {
          element.classList.add(this.getAnimationClass(this.winner, index))
          setTimeout(() => {
            element.style.transform = `rotateX(${this.animationClass[index].rotate}deg)`
          }, 2000)
        })
      }, 0)
      // 因為動畫時間為 3s ,所以這里4s后獲取結果,其實結果早就定下了,只是何時顯示,告訴用戶
      setTimeout(() => {
        this.loading = false
        console.log(
          `恭喜你獲得了${this.winner}-----------------------------------`
        )
      }, 4000)
    },
    //獲取對應的class樣式,并儲存起來,方便后來的class移除
    getAnimationClass(winner, index) {
      //如果是后端指定獎品
      if (winner !== null) {
        this.animationClass[index] = {
          num: winner,
          class: `wr${winner}`,
          rotate: (360 / this.list.length) * -winner
        }
        //對應css樣式中定義的class屬性值,如果有更多的話可以繼續添加  case 8:   return 'wr8'
        return `wr${winner}`
      } else {
        /* 此處是當不中獎時,那么3個輪盤會自動隨機 */
        let ran = this.random(0, this.list.length - 1)
        /* 此處是為了避免隨機時的3個數字一樣,因為不中獎的時候3個數字是不可能一樣的 */
        if (this.animationClass[0]) {
          while (this.animationClass[0].num === ran) {
            ran = this.random(0, this.list.length - 1)
          }
        }
        this.animationClass[index] = {
          num: ran,
          class: `wr${ran}`,
          rotate: (360 / this.list.length) * -ran
        }
        //對應css樣式中定義的class屬性值,如果有更多的話可以繼續添加  case 8:   return 'wr8'
        return `wr${ran}`
      }
    },
    //隨機一個整數的方法
    random(min, max) {
      return parseInt(Math.random() * (max - min + 1) + min)
    }
  }
}
</script>

<style lang="scss" scoped>
$time: 3s; //老虎機的轉動時間 3s 后停止,修改后需要在計時器 setTimeout 中修改對應獲獎提示的時間
.overall {
  perspective: 3000px;
}
/* 抽獎按鈕 */
.chou {
  user-select: none;
  cursor: pointer;
  padding: 10px;
  margin: 20px auto;
  color: white;
  background: #19aca4;
  width: 100px;
  font-size: 20px;
  border-radius: 5px;
  box-shadow: 0 0 10px #ccc;
  &:active {
    transform: scale(0.9);
  }
}
.cj-box {
  height: 100px;
  background: white;
  box-shadow: 0 0 10px #ccc;
  border-radius: 5px;
  padding: 20px 40px;
  display: flex;
  .cj-block {
    width: 100px;
    overflow: hidden;
    box-sizing: border-box;
    margin: 0 1px;
    .gundong {
      display: inline-block;
      transform-style: preserve-3d;
      width: 100px;
      height: 100%;
      list-style: none;
      padding: 0;
      position: relative;
      perspective: 3000px;
      /* 每個獎品格子的樣式 */
      li {
        transform-style: preserve-3d;
        // transform-origin: 50% 0%;
        display: block;
        height: 100px;
        line-height: 100px;
        font-weight: bold;
        font-size: 30px;
        position: absolute;
        width: 100%;
        height: 100%;
        color: #fcfcfc;
      }
    }
  }
}
/* 給每個滾動添加一個延遲動畫效果 */
.cj-block:nth-child(1) ul {
  animation-delay: 0.2s;
  li {
    background: #14e4b7;
  }
}
.cj-block:nth-child(2) ul {
  animation-delay: 0.5s;
  li {
    background: #14dde4;
  }
}
.cj-block:nth-child(3) ul {
  animation-delay: 0.8s;
  li {
    background: #14e4b7;
  }
}
/* 下面的css樣式為每個獎品的旋轉動畫,這里寫了對應8個獎品的動畫,如果想要更多的話,可以添加 */
/* 例如: .wr8  @keyframes play8 */
.wr0,
.wr1,
.wr2,
.wr3,
.wr4,
.wr5,
.wr6,
.wr7 {
  /* 每個轉動的時間 */
  animation-duration: $time;
  animation-timing-function: ease;
  animation-fill-mode: both;
  animation-iteration-count: 1;
}
.wr0 {
  animation-name: play0;
}
.wr1 {
  animation-name: play1;
}
.wr2 {
  animation-name: play2;
}
.wr3 {
  animation-name: play3;
}
.wr4 {
  animation-name: play4;
}
.wr5 {
  animation-name: play5;
}
.wr6 {
  animation-name: play6;
}
.wr7 {
  animation-name: play7;
}
@keyframes play0 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * 0));
  }
}
@keyframes play1 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * -1));
  }
}
@keyframes play2 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * -2));
  }
}
@keyframes play3 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * -3));
  }
}
@keyframes play4 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * -4));
  }
}
@keyframes play5 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * -5));
  }
}
@keyframes play6 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * -6));
  }
}
@keyframes play7 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * -7));
  }
}
</style>
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容