当前位置: 首页 > 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>

二、最后效果

在这里插入图片描述


总结

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

相关文章:

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

基于VUE2转盘组件的开发 文章目录 基于VUE2转盘组件的开发前言一、开发步骤1.组件布局2.布局样式3.数据准备 二、最后效果总结 前言 因为之前的转盘功能是图片做的&#xff0c;每次活动更新都要重做UI和前端&#xff0c;为了解决这一问题进行动态配置转盘组件开发&#xff0c;…...

使用pip或conda离线下载安装包,使用pip或conda安装离线安装包

使用pip或conda离线下载安装包&#xff0c;使用pip或conda安装离线安装包 一、使用pip离线下载安装包1. 在有网络的机器上下载包和依赖2. 传输离线安装包 二、在目标机器上离线安装pip包三、使用conda离线下载安装包1. 在有网络的机器上下载conda包2. 传输conda包或环境包3. 在…...

产品访问分析

1、DWD产品访问明细 1.1、用户产品权限数据 --用户产品权限数据INSERT OVERWRITE TABLE temp_lms.dm_lms_platform_usergroup_app_tmpselect 仓储司南 as pro_name,CCSN as pro_code,c.user_name as user_name,d.account_name …...

【算法】代码随想录之链表(更新中)

文章目录 前言 一、移除链表元素&#xff08;LeetCode--203&#xff09; 前言 跟随代码随想录&#xff0c;学习链表相关的算法题目&#xff0c;记录学习过程中的tips。 一、移除链表元素&#xff08;LeetCode--203&#xff09; 【1】题目描述&#xff1a; 【2】解决思想&am…...

react 18中,使用useRef 获取其他组件的dom并操作节点,flushSync强制同步更新useState

React 不允许组件访问其他组件的 DOM 节点。甚至自己的子组件也不行&#xff01;这是故意的。Refs 是一种脱围机制&#xff0c;应该谨慎使用。手动操作 另一个 组件的 DOM 节点会使你的代码更加脆弱。 相反&#xff0c;想要 暴露其 DOM 节点的组件必须选择该行为。一个组件可以…...

Jupyter Notebook基础:用IPython实现动态编程

Jupyter Notebook基础&#xff1a;用IPython实现动态编程 1. 引言 Jupyter Notebook是一个基于Web的交互式计算环境&#xff0c;允许用户创建和共享包含实时代码、方程式、可视化和文本叙述的文档。它广泛应用于数据清洗与转换、数值模拟、统计建模、机器学习以及其他数据科学…...

Python 爬虫:使用打码平台来识别各种验证码:

本课程使用的是 超级鹰 打码平台&#xff0c; 没有账户的请自行注册&#xff01; 超级鹰验证码识别-专业的验证码云端识别服务,让验证码识别更快速、更准确、更强大 使用打码平台来攻破验证码难题&#xff0c; 是很简单容易的&#xff0c; 但是要钱&#xff01; 案例代码及测…...

理解算法复杂度:空间复杂度详解

引言 在计算机科学中&#xff0c;算法复杂度是衡量算法效率的重要指标。时间复杂度和空间复杂度是算法复杂度的两个主要方面。在这篇博客中&#xff0c;我们将深入探讨空间复杂度&#xff0c;了解其定义、常见类型以及如何进行分析。空间复杂度是衡量算法在执行过程中所需内存…...

浅析Kafka Streams消息流式处理流程及原理

以下结合案例&#xff1a;统计消息中单词出现次数&#xff0c;来测试并说明kafka消息流式处理的执行流程 Maven依赖 <dependencies><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-streams</artifactId><exclusio…...

QGroundControl的总体架构,模块化设计和主要组件的功能。

QGroundControl 总体架构详细描述 QGroundControl (QGC) 作为一个开源地面控制站软件&#xff0c;其设计原则是模块化、高扩展性和高可维护性。 总体架构 QGroundControl 由多个层次构成&#xff0c;每个层次负责不同的功能。这种分层结构确保了系统的高内聚性和低耦合性。 …...

oracle 表空间文件迁移

表空间文件迁移 背景 由于各种原因&#xff0c;在实际工作中可能会出现oracle服务器数据盘空间被占满的情况&#xff0c;这个时候单纯的添加新磁盘&#xff0c;后续表空间文件放新盘的方案已经不适用了&#xff0c;因为源盘已经占用满了&#xff0c;数据库服务会异常&#xf…...

JVM学习(day1)

JVM 运行时数据区 线程共享&#xff1a;方法区、堆 线程独享&#xff08;与个体“同生共死”&#xff09;&#xff1a;虚拟机栈、本地方法栈、程序计数器 程序计数器 作用&#xff1a;记录下次要执行的代码行的行号 特点&#xff1a;为一个没有OOM&#xff08;内存溢出&a…...

js项目生产环境中移除 console

1、terser-webpack-plugin webpack 构建的项目中安装使用 安装&#xff1a; npm install terser-webpack-plugin --save-dev 配置 在webpack.config.js文件中 new TerserPlugin({terserOptions: {output: {comments: false, // 去除注释},warnings: false, // 去除黄色警告,co…...

ROS2 + 科大讯飞 初步实现机器人语音控制

环境配置&#xff1a; 电脑端&#xff1a; ubuntu22.04实体机作为上位机 ROS版本&#xff1a;ros2-humble 实体机器人&#xff1a; STM32 思岚A1激光雷达 科大讯飞语音SDK 讯飞开放平台-以语音交互为核心的人工智能开放平台 实现步骤&#xff1a; 1. 下载和处理科大讯飞语音模…...

HTML5新增的input元素属性:placeholder、required、autofocus、min、max等

HTML5 大幅度地增加与改良了 input 元素的属性&#xff0c;可以简单地使用这些属性来实现 HTML5 之前需要使用 JavaScript 才能实现的许多功能。 下面将详细介绍这些新增的 input 元素的属性。 属性说明属性说明placeholder在输入框显示描述性或提示性文本autocomplete是否保…...

Cornerstone3D导致浏览器崩溃的踩坑记录

WebGL: CONTEXT_LOST_WEBGL: loseContext: context lost ⛳️ 问题描述 在使用vue3vite重构Cornerstone相关项目后&#xff0c;在Mac本地运行良好&#xff0c;但是部署测试环境后&#xff0c;在window系统的Chrome浏览器中切换页面会导致页面崩溃。查看Chrome的任务管理器&am…...

【鸿蒙学习笔记】Stage模型

官方文档&#xff1a;Stage模型开发概述 目录标题 Stage模型好处Stage模型概念图ContextAbilityStageUIAbility组件和ExtensionAbility组件WindowStage Stage模型-组件模型Stage模型-进程模型Stage模型-ArkTS线程模型和任务模型关于任务模型&#xff0c;我们先来了解一下什么是…...

Docker进入MongoDB

先是命令行开启docker镜像&#xff0c;然后进入docker镜像&#xff0c;这是两步 进入之后&#xff0c;开头会变成root&#xff0c;我的理解是进入了另一个linux系统了&#xff0c;直接执行相应的软件 这里直接use databse就是进入了&#xff0c;据说MongoDB是慢启动&#xff0c…...

APP与API:魔法世界的咒语与念咒者

1. 什么是API&#xff1f; API&#xff0c;即应用程序编程接口&#xff08;Application Programming Interface&#xff09;&#xff0c;就像是魔法世界中的咒语。API是两个独立软件系统之间进行通信和数据交换的桥梁。通过API&#xff0c;一个软件系统可以调用另一个软件系统中…...

云计算安全需求分析与安全保护工程

云计算基本概念 云计算&#xff08;Cloud Computing&#xff09;是一种通过互联网提供计算资源和服务的技术。它允许用户按需访问和使用计算资源&#xff0c;如服务器、存储、数据库、网络、安全、分析和软件应用等&#xff0c;而无需管理底层基础设施。以下是云计算的基本概念…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

uniapp 字符包含的相关方法

在uniapp中&#xff0c;如果你想检查一个字符串是否包含另一个子字符串&#xff0c;你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的&#xff0c;但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式

简介 在我的 QT/C 开发工作中&#xff0c;合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式&#xff1a;工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...