转盘绘制-canvas

<template>
  <div class="pie-wrapper" ref="pieWrapper">
    <!-- 抽奖按钮 -->
    <slot name="start"></slot>
    <!-- canvas -->
    <canvas
      class="pie"
      :height="diameter * canvasScal"
      :width="diameter * canvasScal"
      :style="style"
      ref="canvas"
      @transitionend="handleEnd"
    ></canvas>
  </div>
</template>

<script>
export default {
  name: 'LuckyPie',
  data () {
    return {
      rotate: 0,
      rotating: false,
      transition: false,
      // 直径(宽度)
      diameter: 0,
      // canvas提高清晰度放大倍数
      canvasScal: 3
    }
  },
  computed: {
    style () {
      const duration = this.plateConfig.duration || 4000
      let style = `transform: rotate(${this.rotate}deg);`
      if (this.transition) {
        style += `transition-duration: ${duration}ms;`
        style += `transition-property: transform;`
      }
      return style
    },
    luckyLength () {
      return this.datas.length
    },
    sliceWidth () {
      return 360 / this.luckyLength
    },
    radius () {
      if (!this.diameter) return 0
      return this.diameter / 2
    },
    grideAngle () {
      return (Math.PI / this.luckyLength) * 2
    }
  },
  props: {
    // 转盘基础配置
    plateConfig: {
      type: Object,
      default: () => {
        return {
          isOffset: 1,
          // 转动圈数
          rotateNumber: 5,
          duration: 4000
        }
      }
    },
    // 奖项列表
    datas: {
      type: Array,
      default () {
        return []
      }
    },
    target: {
      type: [Number, String],
      default: -1
    }
  },
  watch: {
    target (newVal) {
      if (newVal > -1) this.start()
    },
    datas: {
      handler (n) {
        this.drawImage(n)
      },
      deep: true
    }
  },
  mounted () {
    this.diameter = this.$refs.pieWrapper.clientWidth
    this.drawImage(this.datas)
  },
  methods: {
    handleEnd () {
      this.rotating = false
      this.$emit('roll-end')
    },
    start () {
      this.$nextTick(() => {
        if (!this.rotating && this.target >= 0) {
          // 重复圈数
          const rotateNumber = this.plateConfig.rotateNumber || 0
          this.rotating = true
          this.transition = true
          const index = this.datas.findIndex(item => item.position === this.target)
          // 转到对应位置的偏移度
          let rotate = 360 - this.sliceWidth * index
          if (this.plateConfig.isOffset) { // 指针指到线上,需回退一般单个角度
            rotate = Math.floor(rotate - this.sliceWidth / 2)
          }
          // 随机偏移度(在单个奖项内的偏移)
          const range = Math.floor(this.sliceWidth / 3 * 2)
          let randomOffset = -range + Math.round(Math.random() * range * 2)
          // 旋转总角度
          this.rotate = rotateNumber * 360 + randomOffset + rotate
        }
      })
    },
    drawImage (datas) {
      if (!this.diameter) return

      let singleWidth = Math.floor(Math.sin(this.grideAngle / 2) * this.radius)
      const ctx = this.$refs['canvas'].getContext('2d')
      const that = this
      datas.map((item, index) => {
        ;(arg => {
          const img = new Image()
          img.src = datas[arg].url
          img.onload = () => {
            ctx.save()
            ctx.translate(that.radius * this.canvasScal, that.radius * this.canvasScal)
            if (this.plateConfig.isOffset) { // 偏移,指针指向线上
              ctx.rotate((arg * 2 * Math.PI) / datas.length + this.grideAngle / 2)
            } else { // 偏移,指针指向中间
              ctx.rotate((arg * 2 * Math.PI) / datas.length)
            }
            ctx.drawImage(
              img, -singleWidth * this.canvasScal, -that.radius * this.canvasScal,
              singleWidth * 2 * this.canvasScal, that.radius * this.canvasScal
            )
            ctx.restore()
          }
        })(index)
      })
    }
  }
}
</script>
<style lang="scss" scoped>
.pie-wrapper {
  position: relative;
  width: 100%;
  height: 100%;
  .pie {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    z-index: 1;
    transform: rotate(0deg);
  }
}
</style>

使用说明

 <div class="plate-box">
     <act-lucky-pie class="pie-use" v-if="finishPlateConfig" :datas="prizeImgArr" :target="prizePosition" :plateConfig="plateConfig"
                    @rollEnd="rollEnd">
         <template #start>
<div class="lucky-pointer" @click="handleDraw"></div>
         </template>
     </act-lucky-pie>
</div>
export default {
  components: { ActLuckyPie },
	data() {
    return {,
      prizeImgArr: [],
      plateConfig: {
       	isOffset: 1,
        rotateNumber: 5,
        // 转动位置
        prizePosition: -1,
        // 转盘基础设置
        finishPlateConfig: false
      }
    };
  },
  methods: {
    rollEnd() {
      // 转盘转动动画结束,显示弹窗应在此设置,建议延迟500ms显示
    },
    handleDraw() {
     // 抽奖请求
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值