欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue3實現(xiàn)轉(zhuǎn)盤抽獎效果的示例詳解

 更新時間:2023年10月09日 11:02:59   作者:Liben  
這篇文章主要為大家詳細介紹了如何通過Vue3實現(xiàn)簡單的轉(zhuǎn)盤抽獎效果,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的可以了解一下

前言

之前的文章有寫過九宮格抽獎功能【 Vue3實現(xiàn)九宮格抽獎功能 】, 有興趣的可以看一看給個贊啥的,今天我們看看如何實現(xiàn)一個轉(zhuǎn)盤抽獎功能。

之前公司也寫過轉(zhuǎn)盤抽獎功能,實現(xiàn)流程大概是設(shè)計師出個轉(zhuǎn)盤抽獎設(shè)計圖,禮品固定,后臺請求接口返回中獎數(shù)據(jù),然后前端去實現(xiàn)動效,這么做的缺點就比較固定,比如獎品無法配置,中獎概率也無法配置。我在各大網(wǎng)站都逛了一圈,基本上都上這種固定的抽獎配置,很少有動態(tài)配置獎品和中獎概率的轉(zhuǎn)盤抽獎,基于此我今天就抽空來實現(xiàn)一個可動態(tài)配置獎品的轉(zhuǎn)盤抽象功能。

功能與效果圖

功能:

1、禮物可后臺動態(tài)配置

2、轉(zhuǎn)盤獎品布局和背景動態(tài)生成(重點)

3、轉(zhuǎn)動速度和時間可配置,速度先快后慢

4、記錄上一次停止位置,開始轉(zhuǎn)動不重置,轉(zhuǎn)動位置最終停止在轉(zhuǎn)盤指針中間

5、動畫停止可觸發(fā)中獎提示或?qū)崿F(xiàn)其它功能

6、中獎概率可配置(這里先不管,后面有機會總結(jié)一些抽獎算法)

效果圖如下:

實現(xiàn)

第一步:轉(zhuǎn)盤布局

思路:首先需要一個轉(zhuǎn)盤,然后需要一個抽獎按鈕定位在中間,按鈕定位在中間很簡單,難的是扇形背景顏色如何實現(xiàn),獎品如何均勻分布在每個扇形中間,因為獎品和可配置的,所以扇形大小可變化的。

<div id="app" v-cloak>
  <div class="container">
    <!-- 背景 -->
    <div class="prize-list" ref="prizeWrap" :style="bgColor">
      <!-- 獎品列表 -->
      <div class="prize-item" v-for="(item, index) in prizeList" :style="prizeStyle(index)">
        <img :src="item.pic" alt="">
        <p>{{ item.name }}</p>
      </div>
    </div>
    <div class="btn" @click="start"></div>
  </div>
</div>

第一個難點:如何繪制扇形背景,而且扇形數(shù)量動態(tài)?扇形背景我們可以使用css的裁剪屬性clip-path 或者css中的錐形漸變函數(shù)conic-gradient()來實現(xiàn),這里我們使用conic-gradient()函數(shù)實現(xiàn)扇形背景更為簡單。由于獎品可配置數(shù)量不定,所以背景的扇形我們可以動態(tài)計算生成,這里我們用到了vue3的計算屬性computed實現(xiàn)。

bgColor 計算屬性實現(xiàn)如下:

// 計算繪制轉(zhuǎn)盤背景
const bgColor = computed(() => {
  const _len = state.prizeList.length
  const colorList = ['#5352b3', '#363589']
  let colorVal = ''
  for (let i = 0; i < _len; i++) {
    colorVal += `${colorList[i % 2]} ${rotateAngle.value * i}deg ${rotateAngle.value * (i + 1)}deg,`
  }
  return `
    background: conic-gradient(${colorVal.slice(0, -1)});
  `
})

根據(jù)獎品數(shù)組長度計算出每個扇形的角度,然后顏色可配置,rotateAngle計算屬性為所有獎品平均分布在圓上的角度,這樣一個轉(zhuǎn)盤的背景就動態(tài)繪制成功了。

第二個難點:獎品信息如何平均分布在背景扇形里,并且居中顯示呢?這里我們可以利用CSS transform 屬性中的旋轉(zhuǎn)函數(shù)rotate()來實現(xiàn),這里我們要均勻的分布,也需要使用計算屬性來實現(xiàn)。

prizeStyle(index) 計算屬性實現(xiàn)如下:

// 每個獎品布局
const prizeStyle = computed(() => {
  const _degree = rotateAngle.value
  return (i) => {
    return `
      width: ${2 * 270 * Math.sin(_degree / 2 * Math.PI / 180)}px;
      height: 270px;
      transform: rotate(${_degree * i + _degree / 2}deg);
      transform-origin: 50% 100%;
    `
  }
})

動態(tài)計算每個獎品的布局,通過rotate()函數(shù)和獎品索引計算每個div的旋轉(zhuǎn)角度,這里注意旋轉(zhuǎn)的源點,我們所有獎品的div布局是定位再圓心靠上,所以這里要改變旋轉(zhuǎn)原點為底部居中,值為transform-origin: 50% 100%,這里才會均勻分布在背景圓上。

**第三個難點:**因為扇形大小是動態(tài)的,每個獎品div盒子長寬不能太大,要大概限制在扇形里面,這樣禮物圖片和禮物名字的布局才好限制不會超出扇形,我們怎么計算獎品布局的容器div大小呢?其實這里我們只需要計算出div的寬即可,因為高為圓的半徑,那寬度如何確定?

如上圖,我們要計算大概寬度,即計算BC的長度,A點為圓心,∠CAB和邊AC、AB我們已知,實際上這個問題就變得簡單了,知道角度和兩條鄰邊長度,我們可以利用我們高中所學(xué)的正弦余弦函數(shù)來求BC的值了,不會還有人不會吧?然后利用Math對象中的Math.sin(x) 函數(shù)求出BC長度即可,需要注意的是JavaScript中的正弦函數(shù)參數(shù)x是一個數(shù)值(以弧度為單位),而數(shù)據(jù)中是角度值,這里我們只要稍加轉(zhuǎn)化求出∠CAB所對應(yīng)的弧度長即可,這里不會有人不會計算弧長吧?不會吧?

寬度計算如下:

width: ${2 * 270 * Math.sin(_degree / 2 * Math.PI / 180)}px;

_degree為扇形角度,因為正弦函數(shù)只能在直角三角形中使用,所以我們作輔助線構(gòu)造一個直角三角形來計算即可,不清楚的可以重溫下高中知識,正弦余弦函數(shù),高考必考點哦。

這樣布局就基本完成了。

第二步:轉(zhuǎn)動效果

思路:點擊抽獎按鈕請求中獎數(shù)據(jù),然后開始轉(zhuǎn)動轉(zhuǎn)盤,這里抽獎后臺實現(xiàn),前端請求接口即可,暫不討論。

第一個問題:轉(zhuǎn)動效果實現(xiàn)方式有很多,我們可以使用js實現(xiàn),也可以使用css方式實現(xiàn),眾所周知 ,能使用css實現(xiàn)的絕不使用js,優(yōu)勢不用我多說吧,懂得都懂。那動效如何實現(xiàn)呢?這里我們利用過渡屬性transition和旋轉(zhuǎn)函數(shù)rotate()即可輕松的實現(xiàn)轉(zhuǎn)動特效,而且動畫時長和速度可以很方便的控制。

第二個問題:css實現(xiàn)的話動效停止提示和后續(xù)操作如何實現(xiàn)?我們使用了過渡屬性transition,而恰好在js中有個監(jiān)聽過渡屬性結(jié)束的事件transitionend,當(dāng)轉(zhuǎn)動停止時就會觸發(fā)改事件,我們就可以做一些后續(xù)操作包括提示中獎信息等。

部分代碼如下:

const startRun = () => {
  console.log(state.isRunning, totalRunAngle.value)
  // 設(shè)置動效
  prizeWrap.value.style = `
    ${bgColor.value}
    transform: rotate(${totalRunAngle.value}deg);
    transition: all 4s ease;
  `
  // 監(jiān)聽transition動效停止事件
  prizeWrap.value.addEventListener('transitionend', stopRun)
}
const stopRun = (e) => {
  console.log(e)
  state.isRunning = false
  prizeWrap.value.style = `
    ${bgColor.value}
    transform: rotate(${totalRunAngle.value - state.baseRunAngle}deg);
  `
}

點擊開始轉(zhuǎn)動后給轉(zhuǎn)動的div設(shè)置旋轉(zhuǎn)度數(shù)和過渡效果,停止時移除過渡效果,然后旋轉(zhuǎn)角度重置到上一次中獎的角度初始值。

完整Demo代碼

有不理解的地方可以copy代碼到自己本地調(diào)試,完整代碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {  margin: 0; padding: 0; box-sizing: border-box; }
    [v-cloak] {
      display: none;
    }
    .container {
      width: 540px;
      height: 540px;
      /*background: #98d3fc url('https://www.jq22.com/demo/vue-luck-draw-pdmm202010260015/img/bg.a4b976d5.png') no-repeat center / 100% 100%;*/
      /*background: conic-gradient( 
       red 6deg, orange 6deg 18deg, yellow 18deg 45deg, 
       green 45deg 110deg, blue 110deg 200deg, purple 200deg);*/
      margin: 100px auto;
      position: relative;
    }
    .prize-list {
      width: 100%;
      height: 100%;
      border-radius: 50%;
      border: 10px solid #98d3fc;
      overflow: hidden;
    }
    .prize-item {
      /*border: 2px solid red;*/
      position: absolute;
      left: 0;
      right: 0;
      top: -10px;
      margin: auto;
    }
    .prize-item img {
      width: 30%;
      height: 20%;
      margin: 40px auto 10px;
      display: block;
    }
    .prize-item p {
      color: #fff;
      font-size: 12px;
      text-align: center;
      line-height: 20px;
    }
    .btn {
      width: 160px;
      height: 160px;
      background: url('https://www.jq22.com/demo/jquerylocal201912122316/img/btn_lottery.png') no-repeat center / 100% 100%;
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      margin: auto;
      cursor: pointer;
    }
    .btn::before {
      content: "";
      width: 41px;
      height: 39px;
      background: url('https://www.jq22.com/demo/jquerylocal201912122316/img/icon_point.png') no-repeat center / 100% 100%;
      position: absolute;
      left: 0;
      right: 0;
      top: -33px;
      margin: auto;
    }
  </style>
</head>
<body>
  <div id="app" v-cloak>
    <div class="container">
      <div class="prize-list" ref="prizeWrap" :style="bgColor">
        <div class="prize-item" v-for="(item, index) in prizeList" :style="prizeStyle(index)">
          <img :src="item.pic" alt="">
          <p>{{ item.name }}</p>
        </div>
      </div>
      <div class="btn" @click="start"></div>
    </div>
  </div>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <script>
    const { createApp, onMounted, onUnmounted, ref, reactive, toRefs, computed, nextTick } = Vue
    createApp({
      setup () {
        const state = reactive({
          prizeList: [
            { name: '手機', pic: 'https://bkimg.cdn.bcebos.com/pic/3801213fb80e7bec54e7d237ad7eae389b504ec23d9e' },
            { name: '手表', pic: 'https://img1.baidu.com/it/u=2631716577,1296460670&fm=253&fmt=auto&app=120&f=JPEG' },
            { name: '蘋果', pic: 'https://img2.baidu.com/it/u=2611478896,137965957&fm=253&fmt=auto&app=138&f=JPEG' },
            { name: '棒棒糖', pic: 'https://img2.baidu.com/it/u=576980037,1655121105&fm=253&fmt=auto&app=138&f=PNG' },
            { name: '娃娃', pic: 'https://img2.baidu.com/it/u=4075390137,3967712457&fm=253&fmt=auto&app=138&f=PNG' },
            { name: '木馬', pic: 'https://img1.baidu.com/it/u=2434318933,2727681086&fm=253&fmt=auto&app=120&f=JPEG' },
            { name: '德芙', pic: 'https://img0.baidu.com/it/u=1378564582,2397555841&fm=253&fmt=auto&app=120&f=JPEG' },
            { name: '玫瑰', pic: 'https://img1.baidu.com/it/u=1125656938,422247900&fm=253&fmt=auto&app=120&f=JPEG' }
          ], // 后臺配置的獎品數(shù)據(jù)
          isRunning: false, // 是否正在抽獎
          baseRunAngle: 360 * 5, // 總共轉(zhuǎn)動角度 至少5圈
          prizeId: 0, // 中獎id
        })
        const prizeWrap = ref(null)
        // 平均每個獎品角度
        const rotateAngle = computed(() => {
          const _degree = 360 / state.prizeList.length
          return _degree
        })
        // 要執(zhí)行總角度數(shù)
        const totalRunAngle = computed(() => {
          return state.baseRunAngle + 360 - state.prizeId * rotateAngle.value - rotateAngle.value / 2
        })
        // 計算繪制轉(zhuǎn)盤背景
        const bgColor = computed(() => {
          const _len = state.prizeList.length
          const colorList = ['#5352b3', '#363589']
          let colorVal = ''
          for (let i = 0; i < _len; i++) {
            colorVal += `${colorList[i % 2]} ${rotateAngle.value * i}deg ${rotateAngle.value * (i + 1)}deg,`
          }
          return `
            background: conic-gradient(${colorVal.slice(0, -1)});
          `
        })
        // 每個獎品布局
        const prizeStyle = computed(() => {
          const _degree = rotateAngle.value
          return (i) => {
            return `
              width: ${2 * 270 * Math.sin(_degree / 2 * Math.PI / 180)}px;
              height: 270px;
              transform: rotate(${_degree * i + _degree / 2}deg);
              transform-origin: 50% 100%;
            `
          }
        })
        onMounted(() => {
          prizeWrap.value.style = `${bgColor.value} transform: rotate(-${rotateAngle.value / 2}deg)`
        })
        onUnmounted(() => {
          prizeWrap.value.removeEventListener('transitionend', stopRun)
        })
        // 獲取隨機數(shù)
        const getRandomNum = () => {
          const num = Math.floor(Math.random() * state.prizeList.length)
          return num        
        }
        const start = () => {
          if (!state.isRunning) {
            state.isRunning = true
            console.log('開始抽獎,后臺請求中獎獎品')
            // 請求返回的獎品編號 這里使用隨機數(shù)
            const prizeId = getRandomNum()
            console.log('中獎ID>>>', prizeId, state.prizeList[prizeId])
            state.prizeId = prizeId
            startRun()
          }
        }
        const startRun = () => {
          console.log(state.isRunning, totalRunAngle.value)
          // 設(shè)置動效
          prizeWrap.value.style = `
            ${bgColor.value}
            transform: rotate(${totalRunAngle.value}deg);
            transition: all 4s ease;
          `
          // 監(jiān)聽transition動效停止事件
          prizeWrap.value.addEventListener('transitionend', stopRun)
        }
        const stopRun = (e) => {
          console.log(e)
          state.isRunning = false
          prizeWrap.value.style = `
            ${bgColor.value}
            transform: rotate(${totalRunAngle.value - state.baseRunAngle}deg);
          `
        }
        return {
          ...toRefs(state),
          bgColor,
          prizeStyle,
          prizeWrap,
          start
        }
      }
    }).mount('#app')
  </script>
</body>
</html>

以上就是Vue3實現(xiàn)轉(zhuǎn)盤抽獎效果的示例詳解的詳細內(nèi)容,更多關(guān)于Vue3轉(zhuǎn)盤抽獎的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Vue-cli3 項目在安卓低版本系統(tǒng)和IE上白屏問題解決

    詳解Vue-cli3 項目在安卓低版本系統(tǒng)和IE上白屏問題解決

    這篇文章主要介紹了Vue-cli3 項目在安卓低版本系統(tǒng)和 IE 上白屏問題解決,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-04-04
  • 詳細解讀VUE父子組件的使用

    詳細解讀VUE父子組件的使用

    這篇文章主要介紹了詳細解讀VUE父子組件的使用,今天來講一種子父組件深度耦合的方式,使我們不用額外的創(chuàng)建新的組件,也可以達到一些效果,下面與你們分享一下
    2023-05-05
  • vue前端信息詳情頁模板梳理詳解

    vue前端信息詳情頁模板梳理詳解

    這篇文章主要為大家詳細介紹了vue前端信息詳情頁模板梳理,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • vue2創(chuàng)建高復(fù)用組件的方法示例

    vue2創(chuàng)建高復(fù)用組件的方法示例

    Vue2中的高復(fù)用組件通常是指那些設(shè)計得足夠通用,并能多次在項目中重復(fù)使用的組件,本文給大家詳細介紹了vue2創(chuàng)建高復(fù)用組件的方法示例,并通過代碼示例講解的非常詳細,需要的朋友可以參考下
    2024-07-07
  • vue中關(guān)于@media媒體查詢的使用

    vue中關(guān)于@media媒體查詢的使用

    這篇文章主要介紹了vue中關(guān)于@media媒體查詢的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • vue2.0+webpack環(huán)境的構(gòu)造過程

    vue2.0+webpack環(huán)境的構(gòu)造過程

    本文分步驟給大家介紹了vue2.0+webpack環(huán)境的構(gòu)造過程的相關(guān)資料,非常不錯具有參考借鑒價值,需要的朋友可以參考下
    2016-11-11
  • Vue3中v-slot的特性深度剖析

    Vue3中v-slot的特性深度剖析

    在Vue3架構(gòu)里,v-slot 作為作用域插槽的關(guān)鍵支撐,重塑了父子組件間數(shù)據(jù)與方法傳遞的范式,本文主要來和大家剖析一下v-slot的相關(guān)特性,需要的可以了解下
    2025-01-01
  • vue2.0父子組件間傳遞數(shù)據(jù)的方法

    vue2.0父子組件間傳遞數(shù)據(jù)的方法

    本文通過一個小例子給大家介紹了vue2.0父子組件間傳遞數(shù)據(jù)的方法,需要的朋友參考下吧
    2018-08-08
  • Element-ui中元素滾動時el-option超出元素區(qū)域的問題

    Element-ui中元素滾動時el-option超出元素區(qū)域的問題

    這篇文章主要介紹了Element-ui中元素滾動時el-option超出元素區(qū)域的問題,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-05-05
  • vue分割面板封裝實現(xiàn)記錄

    vue分割面板封裝實現(xiàn)記錄

    這篇文章主要為大家詳細介紹了vue分割面板封裝實現(xiàn)記錄,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03

最新評論