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>