前端实现转盘抽奖 - 使用 lucky-canvas 插件

avatar
作者
筋斗云
阅读量:0

目录

需求背景

要求实现转盘转动抽奖的功能:

  1. 只有正确率大于等于 80% 才可以进行抽奖;
  2. “谢谢参与”概率为 90%,“恭喜中奖”概率为 10%;

需求实现

在这里插入图片描述
在这里插入图片描述

实现过程图片示意

在这里插入图片描述

实现代码

安装插件

npm install @https://blog.csdn.net/m0_53562074/article/details/lucky-canvas/vue@latest 

main.js 全局引入组件

import VueLuckyCanvas from '@https://blog.csdn.net/m0_53562074/article/details/lucky-canvas/vue' Vue.use(VueLuckyCanvas) 

实现代码

<template>   <div class="exam-result">     <div class="info">       <div class="progress">         <nut-circleprogress             :progress="(correct / total).toFixed(1) * 100"             :is-auto="true"             color="#ff4d4f"             path-color="#ffeded"         >           <div class="progressDiv">             <div class="accuracy">正确率{{ (correct / total).toFixed(1) * 100 }}%</div>           </div>         </nut-circleprogress>       </div>     </div>     <div class="content">       <div class="result-table">         <div style="padding: 10px 10px 10px 15px">试卷分析</div>       </div>       <div class="result-table">         <div class="item">           <div class="title">题目总量:</div>           <div class="total">{{ total }}</div>           <div class="unit"></div>         </div>       </div>       <div class="result-table">         <div class="item">           <div class="title">正确题数:</div>           <div class="correct">{{ correct }}</div>           <div class="unit"></div>         </div>         <div class="item">           <div class="title">错误题数:</div>           <div class="error">{{ total - correct }}           </div>           <div class="unit"></div>         </div>       </div>     </div>     <div v-if="examType === 'challenge' && (correct / total).toFixed(1) >= 0.8" class="lottery_draw_btn">恭喜您获得抽奖资格 <nut-button type="primary" size="mini" @click="toLotteryDraw">点击进行抽奖</nut-button></div>     <nut-dialog teleport="#app" :title="isShowlotteryDraw ? '点击“开始”抽奖' : ''" content="" v-model:visible="dialogVisible" customClass="task" :noCancelBtn="true" :noOkBtn="true" :closeOnClickOverlay="false">       <nut-icon name="close" @click="dialogVisible = false" />       <LuckyWheel         v-if="isShowlotteryDraw"         class="myLucky"         ref="myLuckyRef"         width="320px"         height="320px"         :prizes="prizes"         :blocks="blocks"         :buttons="buttons"         @start="startCallback"         @end="endCallback"       />       <div v-else class="result" :style="{'--color': lotteryDrawIndex === 1 ? 'red' : '#000'}">{{ lotteryDrawIndex === 1 ? "恭喜中奖" : "谢谢参与" }}</div>     </nut-dialog>   </div>   <fallback></fallback> </template>  <script> import {   reactive, toRefs, ref, getCurrentInstance } from 'vue' import { useRoute } from 'vue-router'  export default {   name: 'result',   setup() {     // const myLuckyRef = ref(null) // 【ref问题】我的代码里这种办法取不到 ref,使用 getCurrentInstance 取 ref     const instance = getCurrentInstance() // 【ref解决】使用 getCurrentInstance 取 ref     const route = useRoute()     const state = reactive({       lotteryDrawIndex: 0, // 最终转盘定格的索引       isShowlotteryDraw: true, // 是否抽奖完成       // 转盘背景配置       blocks: [{         padding: '20px',         imgs: [{           // src: 'https://img.iwave.net.cn/jeep/51c95637a377c3a12d09abe8b0f975e6.png',           src: require('@/assets/images/lottery_draw.png'),           width: 320,           height: 320,           rotate: true         }]       }],       // 每个扇形区域奖品配置       prizes: [...Array(10).keys()].map((index) => ({         fonts: [           {             text: index % 2 === 0 ? '谢谢参与' : '恭喜中奖',             top: '15%',             fontSize: '15px',             fontColor: '#ed1c24',           },         ],         background: index % 2 === 0 ? '#fff5cc' : '#e9d6e9',       })),       // 抽奖按钮配置       buttons: [         { radius: '50px', background: '#d034ac' },         { radius: '45px', background: '#fe97b2' },         {           radius: '35px',           background: '#f04a07',           pointer: true,           fonts: [{ text: '开始', top: '-10px', fontColor: '#fff' }]         }       ],       // 抽奖弹框是否展示       dialogVisible: false     })     // 获取正确题数、总题数     const { correct, total, examType } = route.query      const toLotteryDraw = () => {       state.dialogVisible = true     }      // 点击抽奖按钮会触发star回调     const startCallback = () => {       console.log('"开始抽奖"----', '开始抽奖')       // 调用抽奖组件的play方法开始游戏       // console.log('myLucky.value----', myLuckyRef.value) // 【ref问题】       // myLuckyRef.value?.play() // 【ref问题】        if (instance) {         instance.refs?.myLuckyRef?.play() // 【ref解决】       }       // this.$refs.myLucky.play()  // 【ref】vue2写法         // 模拟调用接口异步抽奖       setTimeout(() => {         // 假设index(谢谢参与90%,恭喜中奖10%)         const index = `${Math.random()}`.slice(2, 3) * 1         state.lotteryDrawIndex = index === 1 ? 1 : 2         // 调用stop停止旋转并传递中奖索引         // this.$refs.myLuckyRef.stop(index)   // 【ref】vue2写法         // myLuckyRef.value?.stop(index) // 【ref问题】         if (instance) {           instance.refs?.myLuckyRef?.stop(state.lotteryDrawIndex) // 【ref解决】         }       }, 3000)     }     // 抽奖结束会触发end回调     const endCallback = (prize) => {       console.log('"结束抽奖"----', '结束抽奖')       console.log(prize)       state.isShowlotteryDraw = false     }      return {       ...toRefs(state),       correct,       total,       examType,       toLotteryDraw,       startCallback,       endCallback     }   } } </script>  <style scoped lang="less"> .exam-result {   .info {     margin: 0 0 5px;     padding: 10px;     background-color: white;      .progress {       display: flex;       flex-direction: column;       align-items: center;       padding: 5px;       position: relative;        .nut-circleprogress {         width: 145px !important;         height: 145px !important;         position: relative;          .progressDiv {           display: flex;           flex-direction: column;           align-items: center;            .accuracy {             color: #00000080;             background-color: #ffeded;             padding: 2px 8px;             font-size: 13px;             border-radius: 5px;           }         }        }        .circle {         position: absolute;         height: 145px;         width: 145px;         background-color: #ffeded;         border-radius: 50%;         top: 5px;         left: 50%;         transform: translate(-50%);          .circle1 {           position: absolute;           height: 115px;           width: 115px;           background-color: #ffffff;           border-radius: 50%;           top: 50%;           left: 50%;           transform: translate(-50%, -50%);         }       }     }      .count {       background-color: #fffbf3;       margin-top: 10px;       padding-top: 5px;       color: #797e79;       font-size: 14px;       display: flex;       justify-content: space-around;        .centerDiv {         display: flex;         align-items: baseline;         justify-content: center;          .number {           margin-right: 5px;           font-size: 20px;           color: #FAAD14;         }          .text {           font-size: 12px;         }       }     }   }    .content {     margin-bottom: 10px;     background: white;     border-bottom: 1px solid #dcdcdc;      .result-table {       display: flex;       font-size: 16px;       font-weight: bolder;       color: #000;        .item {         display: flex;         align-items: baseline;         border-top: 0.5px solid #dcdcdc;         flex: 1;         font-size: 16px;         padding: 10px 10px 10px 15px;         color: #7f7f7f;         font-weight: normal;          &:nth-child(2n+1) {           border-right: 0.5px solid #dcdcdc;         }          .title {           margin-right: 5px;           font-size: 14px;         }          .unit {           font-size: 12px;           margin-left: 5px;         }          .time,         .total {           color: black;           font-size: 16px;         }          .correct {           color: #04be01;           font-size: 18px;         }          .error {           color: red;           font-size: 18px;         }       }     }   }      // 弹框样式   ::v-deep .popup-center.round {     width: 90%;     .nut-dialog {       width: 100%;       padding: 20px 5px;       .nut-dialog__content {         max-height: unset;         .nut-icon-close {           position: absolute;           top: 15px;           right: 15px;         }         // 转盘结束展示结果         .result {           height: 80px;           line-height: 80px;           font-size: 20px;           font-weight: bold;           color: var(--color);         }         // 转盘         .myLucky {           display: inline-block;         }       }     }   }   // 抽奖弹框按钮   .lottery_draw_btn {     height: 25PX;     line-height: 25PX;     padding: 0 10px;     cursor: pointer;     font-size: 16px;     color: red;   } } </style>  

页面效果

在这里插入图片描述
在这里插入图片描述

https://blog.csdn.net/m0_53562074/article/details/lucky-canvas 插件官方文档

https://blog.csdn.net/m0_53562074/article/details/lucky-canvas 插件官网
https://blog.csdn.net/m0_53562074/article/details/lucky-canvas 插件官网文档

可参考文档

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!