小程序大转盘

发布于 2024-05-29  175 次阅读


// 这是组件Wheel.vue
<template>
  <view
    class="wheel-container"
    :style="{ width: width + 'px', height: width + 'px' }"
  >
    <view
      class="wheel-list"
      :style="{
        backgroundImage: 'url(' + bgImg + ')',
        transform: `rotate({degValue || initDeg}deg)`,
      }"
    >
      <view v-for="(item, index) in prizeList" :key="index">
        <!-- {{item}} -->
        <view class="wheel-item">
          <view
            class="wheel-img"
            :style="{
              width: prizeWidth,
              paddingTop: prizePaddingTop,
              transform: `rotate({index * (360 / prizeList.length)}deg)`,
              'transform-origin': itemTransformOrigin,
            }"
          >
            <!-- <image :src="item.img" mode="widthFix" style="height:auto" /> -->

            <!-- <view class="wheel_num" v-if="item.num">
                            {{item.num}}
                        </view> -->
            <view class="wheel_name">
              <view class="" style="font-weight: bold; font-size: 16px">
                {{ item.title[0] || "" }}
              </view>
              <view class="">
                {{ item.title[1] || "" }}
              </view>
            </view>
          </view>
        </view>
      </view>
    </view>
    <view class="wheel-btn">
      <image :src="btnImg" mode="widthFix" class="img" @click="start" />
    </view>
  </view>
</template>

<script>
export default {
  name: "Wheel",
  data() {
    return {
      // degValue: 0, // 旋转角度
      // prizeWidth: 0, // 奖项背景图宽度计算值
      // prizePaddingTop: 0, // 奖项上边距计算值
      // itemTransformOrigin: '', // 奖项旋转原点计算值
      degValue: 0,
      itemTransformOrigin: "",
      prizeWidth: NaN,
      prizePaddingTop: NaN,
      count: 6,
      rotNum: 0,
      onRunning: false,
    };
  },
  props: {
    width: {
      type: Number,
      default: 312,
    }, // 画布大小,默认单位 rpx
    initDeg: {
      type: Number,
      default: 0,
    }, // 初始旋转角度
    rotTimes: {
      type: Number,
      default: 1,
    }, // 抽奖机会次数
    prizeList: {
      type: Array,
      default: () => {},
    }, // 奖品列表
    prizeName: {
      type: String,
      default: "",
    }, // 获奖项名字
    // prizeWidth: {
    //  type: Number,
    //  default: NaN
    // }, // 奖项宽度
    // prizePaddingTop: {
    //  type: Number,
    //  default: NaN
    // }, // 奖项距离圆弧的内边距
    bgImg: {
      type: String,
      default: "/static/wheel_bg.png",
    }, // 背景图
    btnImg: {
      type: String,
      default: "/static/wheel_button.png",
    }, // 按钮图
  },
  mounted() {
    const widthNum = this._getNum(this.width);
    const widthUnit = this._getUnit(this.width);
    const prizeWidth = this.prizeWidth;
    const paddingTop = this.prizePaddingTop;
    this.degValue = this.initDeg;
    this.itemTransformOrigin = `50% {0.5 * widthNum}{widthUnit};`;
    this.prizeWidth = isNaN(prizeWidth)
      ? this._calculatePrizeWidth()
      : prizeWidth;
    this.prizePaddingTop = isNaN(paddingTop)
      ? this._calculatePrizePaddingTop()
      : paddingTop;

    // this.count = this.prizeList.length; // 奖品个数
    this.rotNum = 0; // 当前是第几次抽奖
    this.onRunning = false; // 是否正在抽奖
  },
  methods: {
    init() {},
    getIndexByName(name) {
      // console.log(this.count,"没走吗====", name);
      const list = this.prizeList;
      for (let i = 0; i < this.prizeList.length; i++) {
        // console.log(list[i].name, "name", name);
        if (list[i] && list[i].name == name) return i;
      }
      return -1;
      // const list = this.prizeList;
      // for (let i = 0; i < this.count; i++) {
      //    console.log(list[name].name,"抽奖结果");
      //    if (i == name) return i;
      // }
      // return -1;
    },
    start(e) {
      console.log("开始", this.prizeName, "次数", this.rotTimes);
      if (this.onRunning) return;
      // if (this.rotNum >= this.rotTimes) {
      //    this.emit('onTimesUp')
      //    return;
      // };
      if (this.rotTimes == 0) {
        this.emit("onTimesUp");
        return;
      }
      // if (!this.prizeName) {
      //    throw new Error('请传入抽奖结果名称:prizeName');
      // }
      const index = this.getIndexByName(this.prizeName);
      if (index === -1) {
        throw new Error(
          `抽奖结果名称与抽奖列表配置项不匹配,未找到名称为{this.prizeName}的奖项`
        );
      }
      this.rotNum += 1;
      this.onRunning = true;
      // const degree = index * (360 / (this.count * 2));
      // const degValue = 360 * this.count * this.rotNum - degree;
      // this.degValue = degValue
      // 计算目标角度
      const singleAngle = 360 / this.prizeList.length;
      const targetAngle = singleAngle * index;
      const rounds = 5 * this.rotNum; // 每次增加的圈数
      const totalAngle = rounds * 360 + (360 - targetAngle);
      this.degValue = totalAngle;
      this.emit("onStart", this.prizeName, this.rotNum);
      setTimeout(() => {
        this.done();
      }, 6000);
    },
    done() {
      this.onRunning = false;
      this.emit("onFinish", this.prizeName, this.rotNum);
      // this.onFinish(this.prizeName, this.rotNum);
    },
    _getNum(s) {
      // 获取像素选项数值
      return parseFloat(s);
    },
    _getUnit(s) {
      // 获取像素选项单位
      s += "";
      return (s.match(/[a-z]+/) || [])[0] || "px";
    },
    _calculatePrizeWidth() {
      // 等边三角形内接正方形边长: (4 - 2 * 根号3) * 边长
      const widthNum = this._getNum(this.width);
      const widthUnit = this._getUnit(this.width);
      return (4 - 2 * Math.sqrt(3)) * 0.5 * widthNum + widthUnit;
    },
    _calculatePrizePaddingTop() {
      // 等边三角形一边的中点离过该边两点的圆弧的距离: 边长 - 边长 * (根号3 / 2)
      const widthNum = this._getNum(this.width);
      const widthUnit = this._getUnit(this.width);
      return 0.5 * widthNum - 0.25 * widthNum * Math.sqrt(3) + 20 + widthUnit;
    },
  },
};
</script>

<style lang="scss" scoped>
.wheel-container {
  position: relative;
  margin: auto;
  border-radius: 50%;
  overflow: hidden;
}

.wheel-list {
  position: absolute;
  border-radius: 50%;
  width: inherit;
  height: inherit;
  transition: all 6s ease;
  -webkit-transition: all 6s ease;
  background-size: 100% 100%;
  background-repeat: no-repeat;
}

.wheel-item {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}

.wheel-img {
  position: relative;
  display: block;
  margin: 0 auto;
  text-align: center;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
}

.wheel-img image {
  display: block;
  width: 20px;
  height: 20px;
}

.wheel_num {
  font-family: PingFang SC, PingFang SC;
  font-weight: 600;
  font-size: 28px;
  color: #ff824a;
}
.wheel_name {
  font-family: PingFang SC, PingFang SC;
  font-weight: 400;
  font-size: 12px;
  color: #ff824a;
  white-space: pre-line;
}

.wheel-btn {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.wheel-btn .img {
  width: 68px;
  height: 86.28px;
}
</style>

// 使用组件
<Wheel @onStart="onStart" @onFinish="onFinish" :prizeList="prizeList" :prizeName="prizeName" :rotTimes="totalTimes"></Wheel>

//参数
prizeList: [],
prizeName: '10金币',
totalTimes: 0,
curTimes: 0,
result: '',

// 方法
/*
                @param name 获奖项名字
                @param times 当前转动次数
              */
    onStart(name, times) {
      console.log(name, times, "开始抽奖");
      // 转盘开始转动
      // this.setData({
      //    result: `第{times}次抽奖中,请稍候...`,
      //    curTimes: times++
      // })
      if (this.totalTimes>0) {
        this.totalTimes -= 1;
      }
      //this.getOnStart();   // 自己的方法
    },
    /*
              @param name 获奖项名字
              @param times 当前转动次数
            */
    onFinish(name, times) {
      console.log(name, times, "结束抽奖");
      //this.getOnFinish();  // 自己的方法
      // this.prizeName = Math.floor(Math.random() * 12)
      // console.log(this.prizeName, "下次的结果");
      // 转盘结束转动
      // this.setData({
      //    result: name === '未中奖' ? '很遗憾,差点就中奖了' : `恭喜你,获得{name}`,
      //    prizeName: this.data.prizeList[Math.floor(Math.random() * 6)].name,
      // })
    },

只会写bug的bugming