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

news2024/12/26 13:35:12

基于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.coloradmin.cn/o/1924416.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】多线程_3

文章目录 九、多线程3. C11中的多线程4. 线程的简单封装 未完待续 九、多线程 3. C11中的多线程 Linux中是根据多线程库来实现多线程的&#xff0c;C11也有自己的多线程&#xff0c;那它的多线程又是怎样的&#xff1f;我们来使用一些C11的多线程。 Makefile&#xff1a; te…

防火墙组网与安全策略实验

实验要求&#xff1a; 实现&#xff1a; 防火墙接口配置&#xff1a; 所有接口均配置为三层接口 由于G1/0/3口下为vlan环境&#xff0c;所以防火墙需要配置子接口 &#xff1a; 交换机划分vlan分开生产区和办公区、配置trunk干道 &#xff1a; 安全策略&#xff1a; 生产区访…

【中项第三版】系统集成项目管理工程师 | 第 4 章 信息系统架构④ | 4.7

前言 第4章对应的内容选择题和案例分析都会进行考查&#xff0c;这一章节属于技术相关的内容&#xff0c;学习要以教材为准。本章分值预计在4-5分。 目录 4.7 安全架构 4.7.1 安全威胁 4.7.2 定义与范围 4.7.3 整体架构设计 4.7.4 网络安全架构设计 4.7.5 数据库系统安…

Pod资源-持久化-网络暴露-env

一.资源清单 资源清单组成 apiVersion&#xff1a;v1 #对应不同的k8s版本中的api&#xff1b; kind&#xff1a;Pod #资源的名称&#xff08;service、ConfigMap、hpa.....&#xff09; metadata&#xff1a; #声明资源的元数据信息&#xff08;资源的…

JavaWeb后端学习

Web&#xff1a;全球局域网&#xff0c;万维网&#xff0c;能通过浏览器访问的网站 Maven Apache旗下的一个开源项目&#xff0c;是一款用于管理和构建Java项目的工具 作用&#xff1a; 依赖管理&#xff1a;方便快捷的管理项目以来的资源&#xff08;jar包&#xff09;&am…

Linux - 探索命令行

探索命令行 Linux命令行中的命令使用格式都是相同的: 命令名称 参数1 参数2 参数3 ...参数之间用任意数量的空白字符分开. 关于命令行, 可以先阅读一些基本常识. 然后我们介绍最常用的一些命令: ls用于列出当前目录(即"文件夹")下的所有文件(或目录). 目录会用蓝色…

初学编程不知道怎么选?推荐学习的三种热门编程语言

在当今的社会需求下&#xff0c;市场上最常见、最受欢迎、最广泛应用的编程语言主要有三种&#xff1a;C语言、Java语言和Python语言。 既然要做出选择&#xff0c;我们就需要明白这三种编程语言各自有何特点和区别。 一、特点 C语言 高效与灵活&#xff1a;C语言生成的机器…

Date/SimpleDateFormat

Date 在Java中&#xff0c;java.util.Date 类用于表示日期和时间。它以自1970年1月1日00:00:00 GMT 一来的毫秒数来存储日期和时间信息。 1、构造方法 标准基准时间&#xff08;称为“历元&#xff08;epoch&#xff09;”&#xff1a;即1970年1月1日00:00:00 GMT。 tips: …

4G LTE教程

整体架构 物理层&#xff08;第 1 层&#xff09; 物理层通过空中接口传输来自 MAC 传输信道的所有信息。负责 RRC 层的链路自适应 (AMC)、功率控制、小区搜索&#xff08;用于初始同步和切换目的&#xff09;和其他测量&#xff08;LTE 系统内部和系统之间&#xff09;。 介…

运行在Linux上的程序越来越慢的排查思路

1、通过free -h 排查内存使用情况&#xff0c;是否内存满了 2、通过df -h 排查磁盘的使用情况&#xff0c;磁盘是否没有空间了 3、检查系统资源配置情况&#xff0c;比如使用ulimit -a检查当前会话的资源限制&#xff0c;如最大文件数、打开文件描述符数等&#xff0c;看是否…

vue 前端项目调用后端接口记录

axios中不同的类型的请求附带数据使用的关键字 请求类型关键字示例GETparamsaxios({ method: get, url: example.com, params: { key: value } })POSTdataaxios({ method: post, url: example.com, data: { key: value } })PUTdataaxios({ method: put, url: example.com, dat…

快速读出linux 内核中全局变量

查问题时发现全局变量能读出来会提高效率&#xff0c;于是考虑从怎么读出内核态的全局变量&#xff0c;脚本如下 f open("/proc/kcore", rb) f.seek(4) # skip magic assert f.read(1) b\x02 # 64 位def read_number(bytes):return int.from_bytes(bytes, little,…

深度解读李彦宏的“不要卷模型,要卷应用”

深度解读李彦宏的“不要卷模型&#xff0c;要卷应用” —— AI技术的应用之道 引言 在2024世界人工智能大会的舞台上&#xff0c;李彦宏的“不要卷模型&#xff0c;要卷应用”言论犹如一石激起千层浪&#xff0c;引发了业界对AI技术发展路径的深思。本文将深入探讨这一观点&a…

修改vscode的字体为等宽字符

在文件——首选项——设置 中 搜索 Editor: Font Family 将内容改为下面的 Consolas, Courier New, monospace 之后重启Vscode就行了

vs2017/2019串口Qt Serial Port/modbus使用报错

vs2017/2019 Qt Serial Port/modbus配置 /* * 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 LNK2019 无法解析的外部符号 "__declspec(dllimport) public: __cdecl QModbusTcpClient::QModbusTcpClient(class QObject *)" (__imp_??…

【代码随想录】【算法训练营】【第66天】 [卡码95]城市间货物运输II [卡码96]城市间货物运输III

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 卡码网。 day 66&#xff0c;周五&#xff0c;ding ding~ [卡码95] 城市间货物运输II 题目描述 卡码95 城市间货物运输II 解题思路 前提&#xff1a; 思路&#xff1a; 重点&#xff1a; 代码实现 C语言 …

智慧养殖的智慧网络:构建高效、可扩展的养殖生态

智慧养殖&#xff0c;在国家政策的大力扶持和农业数字化浪潮的推动下&#xff0c;正迅速发展。然而&#xff0c;许多人对它的价值仍持怀疑态度&#xff1a;认为智慧养殖只是昂贵的技术堆砌&#xff0c;短期内看不到经济回报&#xff0c;甚至怀疑其实用性。本文将挑战这些观点&a…

百川工作手机监控企业员工微信行为

在数字化转型的浪潮中&#xff0c;企业沟通方式正经历着前所未有的变革。微信&#xff0c;作为日常交流不可或缺的工具&#xff0c;已成为企业内外协作的重要桥梁。然而&#xff0c;随着业务量的激增&#xff0c;如何有效监控与管理员工在微信上的行为&#xff0c;确保信息安全…

LLM推理优化笔记1:KV cache、Grouped-query attention等

KV cache 对于decoder-only 模型比如现在如火如荼的大模型&#xff0c;其在生成内容的过程中&#xff0c;为了避免冗余计算&#xff0c;会将Transformer里的self-attention的K和V矩阵给缓存起来&#xff0c;这个过程即为KV cache。 decoder-only模型的生成过程是自回归的&…

让人工智能为你的旋律填词,开启音乐新章

在音乐的世界里&#xff0c;旋律如同灵动的精灵&#xff0c;飞舞在我们的心间。但有时&#xff0c;为这美妙的旋律找到最贴切、最动人的歌词&#xff0c;却成为了创作者们的难题。如今&#xff0c;随着科技的进步&#xff0c;人工智能正逐渐成为我们在音乐创作道路上的得力助手…