vue幸运抽奖大转盘的丑绝实现

自己通过canvas+vue(vue不是必备的可以)实现的一个抽奖转盘,重点在实现逻辑,所以样式丑绝。
基本效果图,中间指针可以替换为图片
基本效果图,中间指针可以替换为图片

数据格式

转盘的分块由传入的数组长度确定,分为4,6,8块还是能看的

 {
          id: 1, // 奖品标识,可按需设置
          content: "谢谢参与", // 奖品文本
          prize: "111", //奖品内容
          probability: 0.75, // 中奖概率,也可以不设,由后台决定是否中奖及中了什么奖
          img: "https://img.stringon.com/dd4cffa524bf46f790520a46fefd1726-S375",// 奖品图片,按需设置
        },
        {
          id: 2,
          content: "现金红包2元",
          prize: "222",
          probability: 0.05,
          img: "https://img.stringon.com/c0f57be115e541fd90b8df5ef163110b-S375",
        },
        {
          id: 3,
          content: "现金红包10元",
          prize: "333",
          probability: 0.02,
          img: "https://img.stringon.com/c0f57be115e541fd90b8df5ef163110b-S375",
        },
        {
          id: 4,
          content: "现金红包3元",
          prize: "444",
          probability: 0.04,
          img: "https://img.stringon.com/c0f57be115e541fd90b8df5ef163110b-S375",
        },

html部分

第一个canvas绘制大转盘
在这里插入图片描述
第二个canvas绘制中间转盘,可替换为图片(用drawImage绘制)
在这里插入图片描述

<div class="turn-box">
    <canvas id="chart" ref="turnBox" height="400" width="600">
      你的浏览器不支持HTML5 canvas
    </canvas>
    <canvas id="centerChart" ref="centerChart" height="400" width="600">
      你的浏览器不支持HTML5 canvas
    </canvas>
  </div>

css部分

css部分比较简单,主要用来定位两个canvas之间的位置

.turn-box {
  position: relative;
}
canvas {
  position: absolute;
  left: 0;
  right: 0;
  margin: auto;
  top: 0;
}
#centerChart {
  top: 50px;
}

js部分

重点的总是留在最后,下面是实现代码,注释我会尽量写的清楚一点

// data初始化
data(){
	return{
		canvas: null,
        ctx: null,
        width: 262, // 宽
        height: 262, // 高
        count: 12,//抽奖次数,也是从后台请求
        running: null,//是否正在抽奖
        turnInfo:[] //奖品信息,一般从后台请求
	}
},
computed: {
   //为了方便比较,这里是中奖区域,如果由后台返回中奖信息,那这里就是不需要的
    randList() {
      let start = 0;
      return this.turnInfo.map((item) => {
        start = item.probability + start;
        return Number(start.toFixed(2));
      });
    },
  },
mounted() {
   // 获得canvas上下文
   this.canvas = document.getElementById("chart");
   if (this.canvas && this.canvas.getContext) {
     this.ctx = this.canvas.getContext("2d");
   }
   this.drawCharts(); //绘制
    const that = this;
    // 点击抽奖
    this.canvas.onclick = async function () {
      if (that.count === 0) return; //没有抽奖次数处理逻辑
      if (that.running) return; //正在抽奖中处理逻辑
      const rand = that.getPrize();//获取中奖信息
      that.$refs.turnBox.style.transform = "unset";
      that.$refs.turnBox.style.transition = "all 0s";//先将之前旋转状态重置
      await setTimeout(() => {
        let deg = 1800 + 360 - (360 / that.turnInfo.length) * rand;//计算旋转角度,1800(360*5)为多旋转的角度
        that.$refs.turnBox.style.transform = `rotate(${deg}deg)`;
        that.$refs.turnBox.style.transition = "all 3s ease-in-out";//开始旋转
      });
      console.log(that.turnInfo[rand].content);//输出中奖信息
      that.running = setTimeout(() => {
        clearTimeout(that.running);
        that.running = null;正在抽奖状态重置
        that.count -= 1;//中奖次数减一
      }, 3000);//延迟时间为转盘旋转动画时间
    };
  },
methods:{
// 图表初始化
    initChart() {
      //   这里是对高清屏幕的处理,
      // 方法:先将canvas的width 和height设置成本来的两倍
      //     然后将style.height 和 style.width设置成本来的宽高
      //     这样相当于把两倍的东西缩放到原来的 1/2,这样在高清屏幕上 一个像素的位置就可以有两个像素的值
      //     这样需要注意的是所有的宽高间距,文字大小等都得设置成原来的两倍才可以。
      this.canvas.width = this.width * 2;
      this.canvas.height = this.height * 2;
      this.canvas.style.height = `${this.height}px`;
      this.canvas.style.width = `${this.width}px`;

      this.ctx.translate(0.5, 0.5); // 当只绘制1像素的线的时候,坐标点需要偏移,这样才能画出1像素实线
    },
    //绘制大转盘
    drawTurnBox() {
      let num = this.turnInfo.length;
      this.ctx.font = "bold 18px Microsoft YaHei";
      for (let i = 0; i < num; i++) {
        //根据奖品参数 绘制扇形填充颜色,按需设置
        // ctx.fillStyle = this.turnInfo.colors[i];
        //开始绘制扇形
        this.ctx.save();
        this.ctx.beginPath();
        this.ctx.translate(this.width, this.width);
        // // 从(0, 0)坐标开始定义一条新的子路径
        this.ctx.moveTo(0, 0);
        this.ctx.rotate(((360 / num) * (i + 1) * Math.PI) / 180);
        //arc(x,y,r,起始角,结束角,绘制方向) 方法创建弧/曲线(用于创建圆或部分圆)
        this.ctx.arc(0, 0, this.width, 0, (2 * Math.PI) / num, false);
        this.ctx.arc(0, 0, this.width / 2 - 50, (2 * Math.PI) / num, 0, true);
        if ((i + 1) % 2 == 0) {
          this.ctx.fillStyle = "#ffb820";
        } else {
          this.ctx.fillStyle = "#ffcb3f";
        }
        // this.ctx.lineWidth = 0.5;
        this.ctx.strokeStyle = "transparent";
        this.ctx.stroke();
        this.ctx.fill();
        this.ctx.restore();
      }
    },
    // 绘制文本
    drawText() {
      let num = this.turnInfo.length;
      this.ctx.font = "24px Arial";
      let arc = Math.PI / (num / 2);
      for (let i = 0; i < num; i++) {
        let angle = 0 + i * arc;
        this.ctx.save();
        //奖品默认字体颜色
        // this.ctx.fillStyle = "#fff";
        let text = this.turnInfo[i].content;
        this.ctx.translate(
          this.width + Math.cos(angle + arc / 2) * (this.width - 40),
          this.width + Math.sin(angle + arc / 2) * (this.width - 40)
        );
        this.ctx.rotate(angle + arc / 2 + Math.PI / 2);
        //由于设计的转盘色块是交错的,所以这样可以实现相邻奖品区域字体颜色不同
        // if (i % 2 == 0) {
        //   this.ctx.fillStyle = "#fff";
        // }
        //将字体绘制在对应坐标
        this.ctx.fillText(text, -this.ctx.measureText(text).width / 2, 20);
        //设置字体
        // this.ctx.font = " 14px Microsoft YaHei";
        //把当前画布返回(插入)到上一个save()状态之前
        this.ctx.restore();
        //绘制奖品图片
        if (this.turnInfo[i].img) {
          let img = new Image();
          img.src = this.turnInfo[i].img;
          img.onload=()=>{
            this.ctx.save();
            this.ctx.translate(
              this.width + Math.cos(angle + arc / 2) * this.width,
              this.width + Math.sin(angle + arc / 2) * this.width
            );
            this.ctx.rotate(angle + arc / 2);
            this.ctx.drawImage(img, -this.width / 2 + 10, -50, 42, 94);
            this.ctx.restore();
          }
        }
      }
    },
    // 绘制中间圆盘
    drawCenter() {
      const canvas = document.getElementById("centerChart");
      canvas.style.top = `${63}px`;// 位置自己定啦
      let ctx = null;
      if (canvas && canvas.getContext) {
        ctx = canvas.getContext("2d");
      }
      let arc = Math.PI / (this.turnInfo.length / 2);
      canvas.width = this.width;
      canvas.height = this.height;
      canvas.style.height = `${this.height / 2}px`;
      canvas.style.width = `${this.width / 2}px`;
      ctx.translate(0.5, 0.5);
      ctx.save();
      ctx.fillStyle = "#ffcb3f";
      ctx.beginPath();
      ctx.translate(this.width / 2, this.width / 2);
      ctx.rotate(arc / this.turnInfo.length + Math.PI / 180);
      //这一部分可以通过drawImage直接绘制成图片,保留中心点设置与旋转角度即可,其他的都随意
      ctx.moveTo(0, 40);
      ctx.lineTo(this.width / 2, 40); //绘制指针
      ctx.arc(0, 0, this.width / 2 - 40, 2 * Math.PI, 0, true);//绘制圆
      ctx.lineWidth = 1;
      ctx.strokeStyle = "rgba(0,0,0,0.45)";
      ctx.stroke();
      ctx.fill();
      ctx.restore();
    },
    // 开始绘制
    drawCharts() {
      this.drawCenter(); //绘制中心圆
      this.initChart(); // 图表初始化
      this.drawTurnBox(); // 绘制转盘
      this.drawText(); // 绘制文字
    },
    //获取中将信息,中奖信息由后台返回会比较安全,这里只需要返回中奖的索引就可以
    getPrize() {
      let rand = Number(Math.random().toFixed(2));//中奖随机数
      let prizeIndex = -1;//中奖索引
      let index = 0;
      while (prizeIndex === -1 && index < this.randList.length) {
        prizeIndex = this.randList[index] >= rand ? index : -1;
        index++;
      }
      return prizeIndex;
    },
}

写在最后

以上就是我实现抽奖大转盘的全部代码,写得比较急,代码粗糙,样式我自己都不忍直视,如果我记得并且有时间的话,我还会进行优化的。
项目地址:抽奖转盘demo
ps:推荐一下我自己写的二维码生成器,有输入字符生成和上传.txt文件生成,主要是希望有人使用后给我提出一些修改意见:qrcode-generator

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值