转盘类型抽奖如何实现 需求

需求

常规转动,如ease-in-out

这种方案适合已经得知中奖信息,得到旋转角度,直接旋转就ok

:style="{transform:rotate(360deg),transition:rotate_transition}"

transform:旋转的角度

transition:过渡方式。如:'transform 3s ease-in-out'

mark transition需要事件触发,所以没法在网页加载时自动发生。所以,只要让transfor或transition变动一次,动画就会执行一次

🙋🏻♀️:点击开始,给style赋值就ok了。transform每次赋值只有不一样,才会动。这就是transition的含义:过渡

rotate_deg = 'rotate(360deg)'

rotate_transition = 'transform 3s ease-in-out'

随机控制开始-旋转-结束

上面的方案适合内定中奖名单,不用考虑接口请求,😈。但如果要是随机中奖呢:开始抽奖,去向接口要中奖信息,接口返回中奖奖品,再决定转盘旋转多少。这种旋转时间是不一定的。啥时候结束旋转取决于接口什么时候返回。

效果:启动 - 加速 - 匀速 - 减速 - 停止

mark 动画效果取决于transform:结束到开始的差值,duration内执行完。。结束值大于初始值,就是正转。反之是反转

封装js控制工具

  • rotate.js
/**
 * mark: 入参interval都是秒,这里具体用到的地方*1000了
 * @param el 入参需要旋转的标签元素
 * @param stepDeg 标签元素step时间需要旋转的角度 360度是一圈
 */
export function doInifityRotate(el) {
  // 每一次的旋转,记录旋转的deg,下次更新style需要在totaldeg累加,规则:如果是大于totaldeg,正转。如果小于totaldeg,反转
  var hasDeg = 0
  // 每一次的旋转,记录已经旋转的时间
  var hasDura = 0

  var easeInImpl = (function() {
    return function($el, duration, deg) {
      hasDeg += deg
      hasDura += duration
      $el.style = 'transform:rotate(' + hasDeg + 'deg);' + 'transition:transform ' + duration + 's ease-in'
    }
  })()

  var interval
  var linearForeverImpl = (function() {
    return function($el, stepInterval, stepDeg) {
      interval = setInterval(() => {
        hasDeg += stepDeg
        hasDura += stepInterval
        $el.style = 'transform:rotate(' + hasDeg + 'deg);' + 'transition:transform ' + stepInterval + 's linear'
      }, (stepInterval * 1000))
      console.log('=====interval ' + interval)
    }
  })()

  var linearImpl = (function() {
    return function($el, duration, deg) {
      hasDeg += deg
      hasDura += duration
      $el.style = 'transform:rotate(' + hasDeg + 'deg);' + 'transition:transform ' + duration + 's linear'
    }
  })()

  var easeOutImpl = (function() {
    return function($el, duration, deg, targetDeg, callBack) {
      if (interval) {
        clearInterval(interval)
      }
      // 1、指定的多转的角度
      hasDeg += deg
      hasDura += duration
      // 2、向上取整到360度的倍数
      const lackDeg = 360 - (hasDeg % 360)
      hasDeg += lackDeg
      // 3、角度落到具体扇区
      hasDeg -= targetDeg
      $el.style = 'transform:rotate(' + hasDeg + 'deg);' + 'transition:transform ' + duration + 's ease-out'
      setTimeout(() => {
        callBack()
      }, (duration * 1000 + 50))
    }
  })()

  var stopImpl = (function() {
    return function($el) {
      console.log(hasDura)
      if (interval) {
        clearInterval(interval)
      }
      hasDura = 0
      hasDeg = 0
      $el.style = ''
    }
  })()

  return {
    easeIn: function(duration, deg) {
      // resetImpl(el)
      console.log('=== ease in')
      easeInImpl(el, duration, deg)
    },
    linearForever: function(stepInterval, stepDeg) {
      linearForeverImpl(el, stepInterval, stepDeg)
    },
    linear: function(duration, deg) {
      linearImpl(el, duration, deg)
    },
    /**
     *
     * @param duration 动画时长
     * @param deg 动画角度
     * @param targetDeg 0度起到奖品落到的角度。比如 需要把指针落到第二个扇区,那就需要多转45度
     */
    easeOut: function(duration, deg, targetDeg, callBack) {
      easeOutImpl(el, duration, deg, targetDeg, callBack)
    },
    stop: function() {
      stopImpl(el)
    }
  }
}

  • 使用
// 开始请求网络 获取本次抽奖中奖信息
      this.prize()

// 转盘控制组件
      var rotate = doInifityRotate(this.$refs['wheel'])
      
// 开始旋转:加速旋转
      rotate.easeIn(2, 720)
// 加速2s后,开始匀速旋转
      setTimeout(() => {
        rotate.linearForever(0.01, 6)
      }, 2000)

// 此间,持续关注着网络请求结果。这里控制了阈值4s
      var interval
      var time = 0
      interval = setInterval(() => {
        time++
        if (Object.keys(this.raffledPrize).length > 0) {
          // 抽到奖了
          clearInterval(interval)
          const that = this
          // 具体的奖品在奖盘上的位置角度
          const targetDeg = this.getTargetDeg()
          
          if (time > 4000) {
          // 减速旋转,直到结束,停到奖品对应的角度位置
            rotate.easeOut(2.5, 720, targetDeg, function() {
            // 中奖后的操作
              that.wheelOver()
            })
          } else {
          // 这里表示,如果网络请求很快,从开始旋转到中奖结果回来,不够4s,那就转够4s。然后开始减速-结束
            setTimeout(() => {
              rotate.easeOut(2.5, 720, targetDeg, function() {
                that.wheelOver()
              })
            }, (4000 - time))
          }
        } else if (this.raffleError) {
          // 抽奖请求失败
          clearInterval(interval)
          const that = this
          setTimeout(() => {
            rotate.stop()
            that.isAllowClick = true
          }, 2100)
        }
      }, 1000)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章