vue实现一个转盘抽奖(奖品可配置)

2,130 阅读2分钟

vue实现一个转盘抽奖(奖品可配置)

主要技术

  • vue2
  • canvas

通过图片和canvas绘制出一个转盘

直接上代码

完成样式

image-20221227120244400.png

html

 <div class="wrap">
    <div class="activcont">
      <div class="rotecont">
        <img src="../assets/tyhd.png" ref="tianyi" style="display: none" />
        <img src="../assets/taideng.png" ref="taideng" style="display: none" />
        <img
          src="../assets/yinxiang.png"
          ref="yinxiang"
          style="display: none"
        />
        <img
          src="../assets/youhuiquan200.png"
          ref="youhuiquan200"
          style="display: none"
        />
        <img
          src="../assets/youhuiquan300.png"
          ref="youhuiquan300"
          style="display: none"
        />
        <img src="../assets/hongbao.png" ref="hongbao" style="display: none" />
        <img
          src="../assets/wangyiyuanyinyue.png"
          ref="wangyi"
          style="display: none"
        />
        <div class="turnplate">
          <canvas
            class="item"
            ref="wheelcanvas"
            width="422px"
            height="422px"
          ></canvas>
          <img
            class="pointer"
            src="../assets/turnplate-pointer.png"
            @click="clickRotate"
          />
        </div>
      </div>
    </div>
  </div>

将所需要绘制的图片先放在html中,以便在canvas绘制的时候使用

css

.wrap {
  width: 100%;
  max-width: 640px;
  margin: auto;
}
​
.activcont {
  width: 100%;
  position: relative;
}
​
.rotecont {
  width: 80%;
  position: relative;
  z-index: 2;
  max-width: 512px;
  left: 0px;
  right: 0px;
  margin: auto;
  overflow: hidden;
}
.rotecont .turnplate {
  display: block;
  width: 100%;
  position: relative;
  background-image: url("../assets/turnplate-bg.png");
  background-size: 100% 100%;
}
.rotecont .turnplate canvas.item {
  width: 100%;
  margin-top: 0.2rem;
  transition: all 8s;
}
.rotecont .turnplate img.pointer {
  position: absolute;
  width: 5rem;
  height: 5rem;
  left: 0px;
  right: 0px;
  margin: auto;
  top: 32%;
}

js

转盘数据

turnplate: {
      //奖品名称
        restaraunts: [
          "天翼高清10元",
          "智能音箱",
          "智能台灯",
          "300元购机优惠券",
          "200元购机优惠券",
          "红包",
          "网易云音乐",
        ], 
        //每个奖品区的背景色
        colors: [
          "#ffecd6",
          "#ffdcc5",
          "#ffecd6",
          "#ffdcc5",
          "#ffecd6",
          "#ffdcc5",
          "#ffecd6",
        ], 
        //大转盘外圆的半径
        outsideRadius: 168, 
        //大转盘奖品位置距离圆心的距离
        textRadius: 140, 
        //大转盘内圆的半径
        insideRadius: 30, 
         //开始角度,此转盘开始指在200元购机券
        startAngle: 7,
      },

绘制奖品

 drawRouletteWheel() {
      let canvas = this.$refs.wheelcanvas;
      if (canvas.getContext) {
        //根据奖品个数计算圆周角度
        let arc = Math.PI / (this.turnplate.restaraunts.length / 2);  // 奖品个数 / 2
        let ctx = canvas.getContext("2d");
        //在给定矩形内清空一个矩形
        ctx.clearRect(0, 0, 422, 422);
        //strokeStyle 属性设置或返回用于笔触的颜色、渐变或模式
        ctx.strokeStyle = "#f5b599";
        //font 属性设置或返回画布上文本内容的当前字体属性
        ctx.font = "bold 16px Helvetica";
        for (let i = 0; i < this.turnplate.restaraunts.length; i++) {
          // 初始角度 + 第几个奖品的圆周角度
          let angle = this.turnplate.startAngle + i * arc;
          // 为当前奖品填充背景色
          ctx.fillStyle = this.turnplate.colors[i];
          ctx.beginPath();
          //arc(x,y,r,起始角,结束角,绘制方向) 方法创建弧/曲线(用于创建圆或部分圆)
          //canvas创建內圆(奖品区域)
          ctx.arc(
            211,
            211,
            this.turnplate.outsideRadius,
            angle,
            angle + arc,
            false
          );
          //旋转按钮的內圆
          ctx.arc(
            211,
            211,
            this.turnplate.insideRadius,
            angle + arc,
            angle,
            true
          );
          //绘制以定好的路径
          ctx.stroke();
          //填充当前的路径
          ctx.fill();
          //锁画布(为了保存之前的画布状态)
          ctx.save();
​
          //----绘制奖品开始----
          ctx.fillStyle = "#f3142d";
          //奖品名称
          let text = this.turnplate.restaraunts[i];
​
          //translate方法重新映射画布上的 (0,0) 位置
          ctx.translate(
            211 + Math.cos(angle + arc / 2) * this.turnplate.textRadius,
            211 + Math.sin(angle + arc / 2) * this.turnplate.textRadius
          );
​
          //rotate方法旋转当前的绘图
          ctx.rotate(angle + arc / 2 + Math.PI / 2);
​
          //在画布上绘制填色的文本。文本的默认颜色是黑色
          //measureText()方法返回包含一个对象,该对象包含以像素计的指定字体宽度
          ctx.fillText(text, -ctx.measureText(text).width / 2, 0);
​
​
          //添加对应图标,根据奖品名称去绘制位置
          if (text.indexOf("天翼") >= 0) {
            let img = this.$refs.tianyi;
            img.onload = function () {
              ctx.drawImage(img, -25, 10);
            };
            ctx.drawImage(img, -25, 10);
          } else if (text.indexOf("台灯") >= 0) {
            let img = this.$refs.taideng;
            img.onload = function () {
              ctx.drawImage(img, -25, 10);
            };
            ctx.drawImage(img, -25, 10);
          } else if (text.indexOf("音箱") >= 0) {
            let img = this.$refs.yinxiang;
            img.onload = function () {
              ctx.drawImage(img, -25, 10);
            };
            ctx.drawImage(img, -25, 10);
          } else if (text.indexOf("200") >= 0) {
            let img = this.$refs.youhuiquan200;
            img.onload = function () {
              ctx.drawImage(img, -25, 10);
            };
            ctx.drawImage(img, -25, 10);
          } else if (text.indexOf("300") >= 0) {
            let img = this.$refs.youhuiquan300;
            img.onload = function () {
              ctx.drawImage(img, -25, 10);
            };
            ctx.drawImage(img, -25, 10);
          } else if (text.indexOf("红包") >= 0) {
            let img = this.$refs.hongbao;
            img.onload = function () {
              ctx.drawImage(img, -25, 10);
            };
            ctx.drawImage(img, -25, 10);
          } else if (text.indexOf("网易") >= 0) {
            let img = this.$refs.wangyi;
            img.onload = function () {
              ctx.drawImage(img, -25, 10);
            };
            ctx.drawImage(img, -25, 10);
          }
​
          //把当前画布返回(调整)到上一个save()状态之前
          ctx.restore();
          //----绘制奖品结束----
        }
      }
    },

绘制奖品成功后,实现抽奖旋转

通过css实现旋转

  1. 给canvas元素设置过渡属性

    .rotecont .turnplate canvas.item {
      transition: all 8s;
    }
    ​
    转盘旋转的秒数
    
  2. 然后通过ref属性 找到canvas,让它旋转起来

      this.$refs.wheelcanvas.style.transform = `rotate(${realDeg}deg)`;
    
  3. 通过随机数模拟接口抽到的奖品

      getRadom(x, y) {
          let min = Math.ceil(x);
          let max = Math.floor(y);
          return Math.floor(Math.random() * (max - min + 1)) + min;
        },
    // 包含最大值和最小值
    
  4. 旋转角度,抽到的奖品

    getPrize(num) {
          switch (num) {
            case 0:
              this.rotate(210, this.turnplate.restaraunts[0]);
              break;
            case 1:
              this.rotate(150, this.turnplate.restaraunts[1]);
              break;
            case 2:
              this.rotate(97, this.turnplate.restaraunts[2]);
              break;
            case 3:
              this.rotate(48, this.turnplate.restaraunts[3]);
              break;
            case 4:
              this.rotate(355, this.turnplate.restaraunts[4]);
              break;
            case 5:
              this.rotate(305, this.turnplate.restaraunts[5]);
              break;
            case 6:
              this.rotate(255, this, this.turnplate.restaraunts[6]);
              break;
          }
        },
    

最后,绘制时机

在生命周期mounted里绘制

this.$nextTick(() => {
    // 当图片都渲染完成,再调绘制
    let img = this.$refs.tianyi
      img.onload = () => {
        this.drawRouletteWheel();
      };
    });

演示

zhuanpan.gif