用vue實現一個簡單的大轉盤抽獎案例
大轉盤
一 轉盤布局
<div class="lucky-wheel-content"><div class="lucky-wheel-prize" :style="wheelStyle" :class="isStart ? 'animated-icon' : ''"@transitionend="onWheelTransitionEnd"><div class="lucky-wheel-prize-item" v-for="(item, index) in prize" :key="item.id":style="{ transform: 'rotate(' + (index * 36) + 'deg)' }"><span>{{ item.reward }}</span><img :src="item.img" alt=""></div></div><div class="lucky-wheel-btn" @click="spinWheel">RODAR</div><img class="lucky-wheel-poiner" src="/img/activity/zphd_zz_s1.avif" /></div>
1.1 :style=“{ transform: ‘rotate(’ + (index * 36) + ‘deg)’ }”
- v-for=“(item, index) in prize”:這表示你正在循環渲染 prize 數組中的每個項目,每個項目都有一個
item 和其在數組中的 index。這個 index 就是每個獎品項的位置。 - index * 36:每個項目的旋轉角度是基于其索引計算的。這里每個項目的旋轉角度是 36 度(360 ÷ 10 = 36),因為假設轉盤有 10 項。通過將 index 乘以 36,每個項目將被均勻地分布在轉盤上。
- :style=“{ transform: ‘rotate(’ + (index * 36) + ‘deg)’ }”:這段代碼為每個
lucky-wheel-prize-item 動態添加一個內聯的 style 屬性,指定它的 transform CSS 樣式。這個
transform 樣式使用 rotate() 函數來旋轉每個獎品項。 - rotate(index * 36):基于 index 計算出每個項目的旋轉角度。比如:
- 對于第一個項目(index = 0),旋轉角度是 0deg。
- 對于第二個項目(index = 1),旋轉角度是 36deg。
- 對于第三個項目(index = 2),旋轉角度是 72deg,以此類推。
效果:通過這種方式,每個 lucky-wheel-prize-item 將會根據它在轉盤上的位置進行旋轉,使它們均勻分布在 360 度的轉盤上。這樣,整個轉盤就呈現出一個環形布局,每個項按順序排列。

計算轉盤旋轉的樣式
通過計算屬性
const wheelStyle = computed(() => {return {transform: `rotate(${rotationAngle.value}deg)`,}
});
rotationAngle是某個響應式變量(可能是通過其他邏輯計算出來的旋轉角度),它的值決定了轉盤旋轉的角度。- 當 rotationAngle.value 發生變化時,computed 屬性會自動重新計算并返回新的樣式對象,其中
rotate(${rotationAngle.value}deg) 的角度會根據最新的 rotationAngle.value 動態更新。 - 然后,這個 wheelStyle 對象可以應用到某個 DOM 元素上,通常是一個轉盤的 style 屬性,以便旋轉轉盤。
<div :style="wheelStyle"><!-- 轉盤內容 -->
</div>
二 轉盤邏輯
2.1 這里最重要的是轉盤總的旋轉角度的設置
spins.value += 5; // 開始的旋轉圈數// 后端返回中獎項的邏輯 隨機數winningIndex.value = Math.floor(Math.random() * totalItems.value);console.log("中獎項:" + winningIndex.value);const anglePerItem = 360 / totalItems.value;let randomAngle = 360 - (winningIndex.value * anglePerItem); // 計算旋轉到中獎的那一項let totalAngle = spins.value * 360 + randomAngle; // 總的旋轉角度console.log("總的旋轉角度:" + totalAngle);// 設置旋轉角度rotationAngle.value = totalAngle;
- let randomAngle = 360 - (winningIndex.value * anglePerItem);
randomAngle 是根據中獎項索引計算出的角度。winningIndex.value 乘以 anglePerItem 得到當前中獎項相對于起始位置的角度,360 - … 計算出從當前起始位置到中獎項的逆時針旋轉角度。




2.2 在請求后端獲取數據時,保持轉動效果
- :class=“isStart ? ‘animated-icon’ : ‘’”
主要是通過加上這個類名來保持這個效果
spins.value += 5;isStart.value = trueapi.getRandom().then(res => {console.log(res);isStart.value = falsewinningIndex.value = res.data// 每個獎品的角度const anglePerItem = 360 / totalItems.value;// 計算中獎項的角度let randomAngle = 360 - (winningIndex.value * anglePerItem);let totalAngle = spins.value * 360 + randomAngle; // 總的旋轉角度// 設置旋轉角度rotationAngle.value = totalAngle;}).finally(() => {console.log('中獎項是:' + prize[winningIndex.value].reward);isSpinning.value = falseisStart.value = false})
api.getRandom()這個是我本地java寫的一個接口,模擬返回一個中獎的索引,同時該接口延遲5s返回數據
@GetMapping("/random")public Result<Integer> getRandom(){try {// 延遲5秒Thread.sleep(5000);} catch (InterruptedException e) {// 處理異常e.printStackTrace();}Random random = new Random();Integer integer = random.nextInt(10); // 生成0到10之間的隨機整數return Result.success(integer); // 返回成功結果}
大轉盤1
三 完整代碼
<div class="lucky-wheel-content"><div class="lucky-wheel-prize" :style="wheelStyle" :class="isStart ? 'animated-icon' : ''"@transitionend="onWheelTransitionEnd"><div class="lucky-wheel-prize-item" v-for="(item, index) in prize" :key="item.id":style="{ transform: 'rotate(' + (index * 36) + 'deg)' }"><span>{{ item.reward }}</span><img :src="item.img" alt=""></div></div><div class="lucky-wheel-btn" @click="spinWheel">RODAR</div><img class="lucky-wheel-poiner" src="/img/activity/zphd_zz_s1.avif" /></div>
const prize = [{id: 1,reward: '0,05',img: '/img/activity/img_zphdjp_s1.png'}, {id: 2,reward: '1,00',img: '/img/activity/img_zphdjp_s1.png'}, {id: 3,reward: '2,00',img: '/img/activity/img_zphdjp_s1.png'}, {id: 4,reward: '3,00',img: '/img/activity/img_zphdjp_s1.png'}, {id: 5,reward: '4,00',img: '/img/activity/img_zphdjp_s1.png'}, {id: 6,reward: '5,00',img: '/img/activity/img_zphdjp_s1.png'}, {id: 7,reward: '15,00',img: '/img/activity/img_zphdjp_s1.png'}, {id: 8,reward: '25,00',img: '/img/activity/img_zphdjp_s1.png'}, {id: 9,reward: '35,00',img: '/img/activity/img_zphdjp_s1.png'}, {id: 10,reward: '75,00',img: '/img/activity/img_zphdjp_s1.png'}
]
const totalItems = ref(prize.length); // 總共有多少個項
const winningIndex = ref(0); // 假設中獎項的索引(從0開始)
const rotationAngle = ref(0); // 當前旋轉角度
const isSpinning = ref(false)
const spins = ref(0) //轉盤轉5圈
const winner = ref(null); // 中獎項
const number = ref(0) //記錄抽獎次數
const isFirst = ref(0)
const isStart = ref(false)
// 計算轉盤旋轉的樣式
const wheelStyle = computed(() => {return {transform: `rotate(${rotationAngle.value}deg)`,}});// 執行旋轉轉盤的操作
const spinWheel = () => {if (isSpinning.value) {return}number.value++;isStart.value = trueconsole.log("當前抽獎次數:" + number.value);isSpinning.value = true; // 開始旋轉winner.value = null; // 重置中獎項winningIndex.value = 0; // 重置中獎索引rotationAngle.value = 0; // 重置旋轉角度spins.value += 5;// api.getRandom().then(res => {// console.log(res);// isStart.value = false// winningIndex.value = res.data// // 每個獎品的角度// const anglePerItem = 360 / totalItems.value;// // 計算中獎項的角度// let randomAngle = 360 - (winningIndex.value * anglePerItem);// let totalAngle = spins.value * 360 + randomAngle; // 總的旋轉角度// // 設置旋轉角度// rotationAngle.value = totalAngle;// }).finally(()=>{// // console.log('中獎項是:'+prize[winningIndex.value].reward );// isSpinning.value=false// })// 后端返回中獎項的邏輯 隨機數winningIndex.value = Math.floor(Math.random() * totalItems.value);// 假設每次轉盤轉5圈// spins.value += 5;setTimeout(()=>{isStart.value = falseisSpinning.value=falseconsole.log('中獎項是:'+prize[winningIndex.value].reward );},2000)// 每個獎品的角度const anglePerItem = 360 / totalItems.value;// 計算中獎項的角度let randomAngle = 360 - (winningIndex.value * anglePerItem);let totalAngle = spins.value * 360 + randomAngle; // 總的旋轉角度// 設置旋轉角度rotationAngle.value = totalAngle;
};// 監聽轉盤動畫結束事件
const onWheelTransitionEnd = () => {isSpinning.value = false; // 旋轉結束// rotationAngle.value=0winner.value = winningIndex.value; // 顯示中獎項// console.log(winner.value, '123');// console.log(rotationAngle.value, '旋轉角度');console.log('中獎項是:'+prize[winner.value].reward );};
.lucky-wheel {.lucky-wheel-content {width: 15rem;height: 15rem;background-image: url('/img/activity/zphd_bj_s1.avif');background-size: 100% 100%;margin: .4rem auto .4rem auto;position: relative;transition: transform 5s ease-in-out;.lucky-wheel-prize {position: absolute;top: 0;right: 0;bottom: 0;left: 0;transition: transform 4s ease-out;/* 控制旋轉動畫 */.lucky-wheel-prize-item {position: absolute;top: 0;right: 0;bottom: 0;left: 0;z-index: 10;width: 2rem;margin: auto;height: 100%;color: #ffff;font-size: .22rem;text-align: center;display: flex;align-items: center;flex-direction: column;&>img {width: 2rem;height: 2rem;}&>span {display: -webkit-box;overflow: hidden;text-overflow: ellipsis;vertical-align: middle;-webkit-line-clamp: 2;-webkit-box-orient: vertical;font-size: .6rem;word-break: break-all;height: 2.8rem;line-height: 2.8rem;}}}@keyframes spin {0% {transform: rotate(0deg);/* 開始時從0度 */}100% {transform: rotate(360deg);/* 結束時旋轉一圈 */}}/* 應用動畫 */.animated-icon {display: inline-block;animation: spin 1s linear infinite;/* 1秒旋轉一圈,永遠循環 */}