移动端canvas扭蛋机

在这里插入图片描述
在这里插入图片描述

<template>
  <div class="component">
    <div class="bg">
      <div class="lotterybg" :style="`background: url(`+canvasUrl+`)center center / cover no-repeat`">
        <canvas
          :style="canvasStyle"
          id="myCanvas"
        ></canvas>
      </div>
    </div>
     <div v-for="(item,index) in prizeList" :key="index">
      <img
      :src="item.picUrl"
      class="imgSrc"
    />
    </div>
      <!-- <img src="https://www.17sucai.com/preview/5730/2018-03-05/2/images/an_go.png" id="start" @click="play" /> -->
    <div id="award" :style="awardStyle"><span id="awardBall" :style="`background: url(`+awardImgUrl+`)center center / cover no-repeat`"> 
    </span></div> 
  </div>
</template>
<script src="./js/point"></script>
// <script src="./js/hidpi-canvas.min"></script>
<script>
import Ball from './js/point'
export default {
  name: 'twistedEgg',
  props: {
    canvasUrl: {
      editor: {
        type: 'image',
        label: '扭蛋机背景图',
      },
      type: String,
      default: 'https://vueweb.oss-cn-qingdao.aliyuncs.com/vueweb/resource/ymm_1632812102882.png',
    },
    isData: {
          type: Boolean,
          default: false,
          editor: {
              type: 'boolean',
              label: '是否数据源'
          }
      },
    dataScoped: {
        type: String,
        default: '',
        editor: {
            type: 'string',
            label: '数据源名称(dataHub)'
        }
    },
    imgUrl: {
      type: Array,
      default () {
        return [
          {
            url: 'https://vueweb.oss-cn-qingdao.aliyuncs.com/vueweb/resource/ymm_1632810357807.png'
          }
        ]
      },
      editor: {
        ignore: true
      }
    },
    canvasWidth: {
      default: 300,
      type: Number,
      editor: {
          type: 'number',
          label: '扭蛋区域的宽',
      }
    },
    canvasHeight: {
      default: 292,
      type: Number,
      editor: {
          type: 'number',
          label: '扭蛋区域的高',
      }
    },
    canvasTop: {
      default: -26,
      type: Number,
      editor: {
          type: 'number',
          label: '扭蛋区域距离顶部的距离',
      }
    },
    canvasLeft: {
      default: 26,
      type: Number,
      editor: {
          type: 'number',
          label: '扭蛋区域距离左侧的值',
      }
    },
     canvasBorderRadius: {
      default: '0',
      type: String,
      editor: {
          type: 'string',
          label: '扭蛋区域的圆角',
      }
    },
    canvasPadding: {
      default: 0,
      type: Number,
      editor: {
          type: 'number',
          label: '扭蛋区域内边距',
      }
    },
    ballWidth: {
      default: 65,
      type: Number,
      editor: {
          type: 'number',
          label: '扭蛋宽',
      }
    },
    ballHeight: {
      default: 66,
      type: Number,
      editor: {
          type: 'number',
          label: '扭蛋高',
      }
    },
    awardPosition: {
        type: String,
        default: ' 50px;height: 50px;top: 380px;left:10px',
        editor: {
            type: 'string',
            label: '出口位置'
        }
    },
    functionPop: {
      type: String,
      editor: {
        type: 'string',
        label: '抽奖完成奖品弹出框方法:',
      },
    },
  },
  computed: {
      // 数据源列表
    prizeList () {
      if (this.isData) {
         if (!this.isEmpty(this.$parent.DataHub) && !this.isEmpty(this.$parent.DataHub[this.dataScoped])) {
            return this.$parent.DataHub[this.dataScoped]
        } else {
            return []
        }
      } else {
          return this.fakeDataSource
      }
    },
    canvasStyle () {
      let style = {
        top: this.canvasTop + 'px',
        left: this.canvasLeft + 'px',
        borderRadius: this.canvasBorderRadius,
        padding: this.canvasPadding + 'px',
      }
      return style
    },
    awardStyle () {
       return `${this.awardPosition};color:${this.itemTextColor};`
    },
  },
  data () {
    return {
      canvas: '',
      ctx: '',
      ballList: [],
      ballNum: '',
      awardList: [],
      scale: 1,
      fakeDataSource: [
        {
            'picUrl': 'https://vueweb.oss-cn-qingdao.aliyuncs.com/platform/5.png',
        },
          {
            'picUrl': 'https://vueweb.oss-cn-qingdao.aliyuncs.com/platform/7.png',
        },
          {
            'picUrl': 'https://vueweb.oss-cn-qingdao.aliyuncs.com/platform/6.png',
        }, {
            'picUrl': 'https://vueweb.oss-cn-qingdao.aliyuncs.com/platform/5.png',
        },
         {
            'picUrl': 'https://vueweb.oss-cn-qingdao.aliyuncs.com/platform/5.png',
        },
          {
            'picUrl': 'https://vueweb.oss-cn-qingdao.aliyuncs.com/platform/7.png',
        },
          {
            'picUrl': 'https://vueweb.oss-cn-qingdao.aliyuncs.com/platform/6.png',
        }, {
            'picUrl': 'https://vueweb.oss-cn-qingdao.aliyuncs.com/platform/5.png',
        },
      ],
      awardImgUrl: 'https://vueweb.oss-cn-qingdao.aliyuncs.com/platform/5.png',
    }
  },
  editorMethods: {
  },
  watch: {
  },
  mounted () {
          this.demo()
  },
  methods: {
    demo () {
      this.canvas = document.getElementById('myCanvas')
      this.scale = window.devicePixelRatio
      this.canvas.width = this.canvasWidth * this.scale
      this.canvas.height = this.canvasHeight * this.scale
      this.canvas.style.width = this.canvas.width / this.scale + 'px'
      this.canvas.style.height = this.canvas.height / this.scale + 'px'
      this.ctx = this.canvas.getContext('2d')
      this.ballList = Array.from(document.getElementsByClassName('imgSrc'))
      this.ballNum = this.ballList.length // 扭蛋机里面的小球数
       for (let i = 0; i < this.ballNum; i++) { // 随机生成各色小球
        this.awardList[i] = new Ball(this.canvas, this.ballList[i], this.ballWidth, this.ballHeight, i) // 新建小球对象
      }
    },
    isEmpty (str) {
      if (typeof str == 'undefined' || str == null || str == '') {
          return true
      } else {
          return false
      }
    },
    play () {
       var award = document.getElementById('awardBall')
       award.removeAttribute('class', 'dropBall')
      if (this.awardList.length === 0) { // 奖池中没有小球
        this.init()
      } else {
        window.clearInterval(this.timer) // 清除计时器
        this.timer = setInterval(() => {
          this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height) // 清空画布
          for (let i = 0; i < this.awardList.length; i++) {
            this.awardList[i].run(this.ctx, this.canvas, this.ballWidth, this.ballHeight)
          } // 使小球运动
        }, 15)
        this.clear()
      }
    },
    init () { // 初始化
      for (let i = 0; i < this.ballNum; i++) { // 随机生成各色小球
        let index = Math.floor(4 * Math.random())
        this.awardList[i] = new Ball(this.canvas, index, this.ballList[index]) // 新建小球对象
      }
      window.clearInterval(this.timer) // 清除计时器
      this.timer = setInterval(() => {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height) // 清空画布
        for (let i = 0; i < this.awardList.length; i++) {
          this.awardList[i].run(this.ctx, this.canvas, this.ballWidth, this.ballHeight)
        } // 使小球运动
      }, 100)
      this.clear()
    },
    clear () {
     var award = document.getElementById('awardBall')
     document.getElementById('awardBall').style.display = 'inline'
      setTimeout(() => {
       window.clearInterval(this.timer) // 清除计时器
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height) // 清空画布
        for (let i = 0; i < this.ballNum; i++) { // 随机生成各色小球
        this.awardList[i] = new Ball(this.canvas, this.ballList[i], this.ballWidth, this.ballHeight, i) // 新建小球对象
      }
       award.setAttribute('class', 'dropBall')
      }, 1500)
        let that = this
      setTimeout(() => {
         if (!that.functionPop) {
            return
          }
          eval(`that.${that.functionPop}()`)
      }, 1600)
    }
  }
}
</script>

<style lang="stylus" rel="stylesheet/stylus" type="text/stylus" scoped>
.component {
  width: 100%;
  height: 100%;
}

.bg {
  position: absolute;
  width: 100%;
  height: 100%;
}

.lotterybg {
  position: absolute;
  background-size: contain;
  overflow: visible;
  width: 100%;
  height: 100%;
}

#myCanvas {
  position: absolute;
  border: none;
  // background-color: rgb(118, 185, 185);
}

#start {
  position: absolute;
  z-index: 3;
  width: 50px;
  margin-top: 370px;
  margin-left: 50%;
}

.imgSrc {
  display:none;
  position: absolute;
}

#award {
  position: absolute;
  border: none;
}
.awardImgUrl{
  width :100%;
  height :100%
}
#awardBall{
  display :none
}
.dropBall {
   content: "";position: absolute;left: 0;top: 0;
   width: 100%;height: 100%;
   display: block;
   background-size: contain;
   animation: drop 1s ease-out forwards;
   -webkit-animation: drop 1s ease-out forwards;
   }
@keyframes drop {
    0% {
        transform: scale(0.7);
    }
    50% {
        transform: scale(1);
    }
    51% {
        transform: translateY(0px);
    }
    100% {
        transform: translateY(10px);
    }
}

@-webkit-keyframes drop {
    0% {
        -webkit-transform: scale(0.7);
    }
    50% {
        -webkit-transform: scale(1);
    }
    51% {
        -webkit-transform: translateY(0px);
    }
    100% {
        -webkit-transform: translateY(10px);
    }
}
</style>

point.js

class Ball {
    constructor (canvas, img, width, height, index) {
    //scale 是为了解决移动端图片模糊问题 有点乱,后期再整理
        this.scale = window.devicePixelRatio
        //小球随机位置设置到容器下半部分,不至于悬浮在上面
        this.x = this.rand(canvas.width - width * this.scale)
        this.y = this.rand(canvas.height - height * this.scale - (canvas.height / 2)) + canvas.height  / 2
        this.img = img
        this.index = index
        if (width) {
          canvas.getContext('2d').drawImage(this.img, this.x, this.y, width * this.scale, height * this.scale)
        }
        this.init()
    }
    init () {
        do {
            this.speedX = this.rand(20) - 10
        } while (this.speedX < 5)
        do {
            this.speedY = this.rand(20) - 10
        } while (this.speedY < 5)
    }
    rand (num) {
        return Math.random() * num
    }
    run (ctx, canvas, width, height) {
        this.x += this.speedX
        this.y += this.speedY
        if (this.x > canvas.width - width * this.scale) {
            this.speedX = -this.speedX
        }
        if (this.x < 0) {
            this.speedX = Math.abs(this.speedX)
        }
        if (this.y > canvas.height - height * this.scale) {
            this.speedY = -this.speedY
        }
        if (this.y < 0) {
            this.speedY = Math.abs(this.speedY)
        }
        canvas.getContext('2d').drawImage(this.img, this.x, this.y, width * this.scale, height * this.scale)
    }
}
export default Ball
请用今天的努力,让明天没有遗憾。
原文地址:https://www.cnblogs.com/cupid10/p/15617588.html