当前位置: 首页 > news >正文

Vue.js学习笔记(五)抽奖组件封装——转盘抽奖

基于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>

二、最后效果

在这里插入图片描述


总结

本文仅仅简单记录了转盘组件的基本实现,仅供学习参考。

http://www.lryc.cn/news/398846.html

相关文章:

  • 使用pip或conda离线下载安装包,使用pip或conda安装离线安装包
  • 产品访问分析
  • 【算法】代码随想录之链表(更新中)
  • react 18中,使用useRef 获取其他组件的dom并操作节点,flushSync强制同步更新useState
  • Jupyter Notebook基础:用IPython实现动态编程
  • Python 爬虫:使用打码平台来识别各种验证码:
  • 理解算法复杂度:空间复杂度详解
  • 浅析Kafka Streams消息流式处理流程及原理
  • QGroundControl的总体架构,模块化设计和主要组件的功能。
  • oracle 表空间文件迁移
  • JVM学习(day1)
  • js项目生产环境中移除 console
  • ROS2 + 科大讯飞 初步实现机器人语音控制
  • HTML5新增的input元素属性:placeholder、required、autofocus、min、max等
  • Cornerstone3D导致浏览器崩溃的踩坑记录
  • 【鸿蒙学习笔记】Stage模型
  • Docker进入MongoDB
  • APP与API:魔法世界的咒语与念咒者
  • 云计算安全需求分析与安全保护工程
  • 七天.NET 8操作SQLite入门到实战 - 第二天 在 Windows 上配置 SQLite环境
  • 操作系统——进程的状态与转换
  • 80. UE5 RPG 实现UI显示技能冷却进度功能
  • Vue2-集成路由Vue Router介绍与使用
  • TemuAPI接口:获取商品详情功能
  • deepstream读取mp4文件及不同类型视频输入bug解决
  • Redis服务器统计和配置信息简介
  • Linux Mac 安装Higress 平替 Spring Cloud Gateway
  • 基于重叠群稀疏的总变分信号降噪及在旋转机械故障诊断中的应用(MATLAB)
  • 【YOLOv8】 用YOLOv8实现数字式工业仪表智能读数(一)
  • 微信小程序---npm 支持