基于VUE2轉盤組件的開發
文章目錄
- 基于VUE2轉盤組件的開發
- 前言
- 一、開發步驟
- 1.組件布局
- 2.布局樣式
- 3.數據準備
- 二、最后效果
- 總結
前言
因為之前的轉盤功能是圖片做的,每次活動更新都要重做UI和前端,為了解決這一問題進行動態配置轉盤組件開發,可以減少一些UI和前端的工作量。
一、開發步驟
1.組件布局
<van-row class="container"><!-- turntableBox 為整個轉盤容器,為正方形,大小由里面元素決定 --><van-col span="24" class="turntableBox"><!-- turntableMain 為轉盤底座,比里面的內容大,顯示為效果圖灰色外圈,但不是空心圓 --><div class="turntableMain" :style="`height:${window.innerWidth * 0.8}px;width:${window.innerWidth * 0.8}px;`"><!-- turntable 為轉動區域,作用是為了不讓外圈一起轉動 --><div ref="turntable" class="turntable":style="`height:${window.innerWidth * 0.8}px;width:${window.innerWidth * 0.8}px;`"><!-- Canvas 轉盤餅圖背景,具體劃分多少塊由獎項決定 --><Canvas /><!-- prizeBox 獎項,高為餅圖的半徑,寬為餅圖半徑里面有多少塊就多少分之一 --><div class="prizeBox"><div class="prizeItem" :style="`width:${perPrize.width}px;height:${perPrize.height}px;transform:translateX(-50%) rotate(-${(perPrize.degree * (index + 1)) - (perPrize.degree / 2)}deg);left:calc(50%)`"v-for="(item, index) in activeInfo.prizeList" :key="index"><p class="title">{{ item.name }}</p><p class="describe">{{ item.describe }}</p><img :src="item.img" style="width: 38%;" /></div></div></div><!-- 啟動按鈕 --><van-image class="go" fit="cover" width="42px" :src="goPointer" @click="go" /></div></van-col><!-- 結果展示列表 --><van-col span="24"><div id="result"></div></van-col></van-row>
2.布局樣式
.turntableBox {margin-top: 10%;.turntableMain {margin: 0 auto;position: relative;border: 10px solid #E5E5E5;border-radius: 100%;}.turntable {transition: all 4s;margin: 0 auto;}.go {position: absolute;top: calc(50% - 31px);left: calc(50% - 21px);}.prizeBox {position: absolute;width: 80%;top: 0;left: calc(50% - 40%);.prizeItem {text-align: center;position: absolute;top: 0;overflow: hidden;text-align: center;transform-origin: center bottom;transform: translateX(-50%);color: #2c3e50;p {margin: 0;padding: 0;}.title {font-size: 18px;margin-top: 12px;}.describe {font-size: 14px;line-height: 28px;white-space: break-spaces;}img {margin-top: 6px;}}}
}
3.數據準備
data 代碼如下:包含頁面功能所需要的變量
data() {return {window,/** 活動設置 */activeInfo: {/** 中獎概率 */probabilities: {"一等獎": 10,"二等獎": 10,"三等獎": 10,"四等獎": 10,},/** 獎品信息 */prizeList: [{name: '一等獎',describe: '一等獎',img: 'https://img01.yzcdn.cn/vant/cat.jpeg'},{name: '未中獎',describe: '未中獎',img: 'https://img01.yzcdn.cn/vant/cat.jpeg'},{name: '二等獎',describe: '二等獎',img: 'https://img01.yzcdn.cn/vant/cat.jpeg'},{name: '未中獎',describe: '未中獎',img: 'https://img01.yzcdn.cn/vant/cat.jpeg'},{name: '三等獎',describe: '三等獎',img: 'https://img01.yzcdn.cn/vant/cat.jpeg'},{name: '四等獎',describe: '四等獎',img: 'https://img01.yzcdn.cn/vant/cat.jpeg'},]},/** 是否正在執行動畫 */isGo: false,/** 執行動畫的對象 */oTurntable: '',/** 即將旋轉的度數 */randomDeg: 0,/** 上一次旋轉的度數 */lastDeg: 0,/** 抽獎次數 */goTimes: 3,/** 獎品圖片 */perPrize: {degree: null,width: null,height: null}}}
created 代碼如下:主要處理角度、寬、高
created() {const params = getAllParams();if (params) {this.params = params;};/** 獎品 */const angle = (360 / this.activeInfo.prizeList.length) / 2; // 對角角度const ratio = Number(Math.sin(angle * (Math.PI * 2 / 360)).toFixed(2)); // 與半徑的比率this.perPrize = {degree: (360 / this.activeInfo.prizeList.length),width: Math.floor((window.innerWidth * ratio)) / 2,/** 高度是直徑的一半 */height: window.innerWidth * 0.8 / 2}},
mounted 代碼如下:獲取轉盤區域DOM元素,方便后面操作
mounted() {this.oTurntable = this.$refs.turntable;},
methods 代碼如下:主要操作方法
/** 點擊抽獎 */go() {/** 正在抽獎,未結束繼續點擊無效 */if (!this.isGo && this.goTimes > 0) {/** 獲取中獎結果,再根據結果去轉動轉盤 */const result = this.generatePrize();/** * 獲取獎項下標* 獎項名字可能會重復,所以需要找到獎項的所有下標保存到數組里* 根據下標數組隨機生成一個數字來決定選擇哪個下標成為最終結果的下標* */const resultIndexArray = this.activeInfo.prizeList.reduce((acc, item, index) => {if (item.name === result) {acc.push(index);}return acc;}, []);const randomResultIndex = Math.floor(Math.random() * resultIndexArray.length);const index = resultIndexArray[randomResultIndex];/** 獎項總和數量 */const length = this.activeInfo.prizeList.length;/** 調用旋轉方法 */this.ratating((360 / length * index) + (360 / length / 2), result);}else if (!this.isGo && this.goTimes <= 0) {this.$toast({message: '抱歉,您的抽獎次數用完了',duration: 3000,});}else {this.$toast('請勿重復點擊')return}},/** 獲取抽獎結果 */generatePrize() {/** 生成一個 0 到 99 之間的隨機數 */const randomNum = Math.floor(Math.random() * 100);let cumulativeProbability = 0;/** 如果概率落在獎項范圍內 */for (const prize in this.activeInfo.probabilities) {cumulativeProbability += this.activeInfo.probabilities[prize];if (randomNum < cumulativeProbability) {/** 返回中獎內容 */return prize;}}// 默認返回未中獎return "未中獎";},/** 該方法能產生[n,m]之間隨機數,決定轉盤轉多少圈 */getRandom(n, m) {let result = Math.floor(Math.floor(Math.random() * (m - n + 1) + n))return result;},/** 旋轉 */ratating(deg, text) {this.goTimes--;this.isGo = true;/** 旋轉圈數 */let turnNumber = this.getRandom(3, 6);/** 記錄這次要旋轉的度數(傳來的度數+圈數) */this.randomDeg = deg + 360 * turnNumber;/*上次指針離初始狀態的度數 + 上次的度數 + 這次要旋轉的度數(這樣的目的是為了每次旋轉都從原點開始,保證數據準確)*/let realDeg = (360 - this.lastDeg % 360) + this.lastDeg + this.randomDeg;/** 為對象添加執行動畫 */this.oTurntable.style.transform = `rotate(${realDeg}deg)`;setTimeout(() => {this.isGo = false;var list = document.getElementById('result');list.innerHTML += /未中獎/.test(text) ? `<p>很遺憾,您${text}!</p>` : `<p>恭喜您,獲得${text}!</p>`;/** 把這次度數存儲起來,方便下一次獲取 */this.lastDeg = realDeg;}, 4000);}
canvas 組件代碼如下:主要使用canvas標簽根據獎項長度進行角度劃分繪畫,
<template><canvas class="canvas" id="canvasImg" :style="`width:${perimeter}px;height: ${perimeter}px;`">您的瀏覽器不支持canvas!</canvas>
</template><script>export default {name: 'Canvas',components: {},data() {return {/** 直徑 */perimeter: 320,}},created() {},mounted() {this.perimeter = window.innerWidth * 0.8;this.drawPie();},methods: {/** 畫餅圖 */drawPie() {const PI = Math.PI;/** 獲取畫布并獲取2d上下文對象 */const canvas = document.getElementById('canvasImg');const ctx = canvas.getContext('2d');/** 假設周長為500 */const perimeter = this.perimeter;/** 半徑 */const radius = perimeter * 0.5;/** 總獎品數,需要根據實際數據長度從父組件傳入 */const prizeTotal = 6;/** 每個扇形的角度=360度 / 總獎品數 */const degree = 360 / prizeTotal;/** 畫布寬高 */canvas.width = perimeter;canvas.height = perimeter;/** 根據獎品數把圓形分成等份的扇形 */for (let i = 0; i < prizeTotal; i++) {/** 奇偶顏色 */const color = i % 2 === 0 ? "#F8D383" : "#F8E2BC";/** 開始一條新路徑 */ctx.beginPath();/** 設置路徑起點 */ctx.moveTo(radius, radius);/** 填充顏色 */ ctx.fillStyle = color;/** 繪制扇形 (圓心坐標,圓心坐標,半徑,扇形起始角度,扇形終止角度) */ctx.arc(radius, radius, radius, (270 - degree + (degree * i)) * PI / 180, (270 - degree + degree + (degree * i)) * PI / 180);/** 自動繪制一條當前點到起點的直線,形成一個封閉圖形,省卻使用一次moveTo方法。 */ctx.closePath();/** 閉合路徑 */ctx.fill();}}},
}
</script><style lang="less"></style>
二、最后效果
總結
本文僅僅簡單記錄了轉盤組件的基本實現,僅供學習參考。