最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • JS-VUE-老虎机抽奖

    正文概述 掘金(依旧优雅)   2020-12-08   488

    前言

    好久没动弹了,最近刚好写了个“老虎机”,发上来和大家一起讨论一下,有哪些实现方案以及一些实现方案的优劣度,本文主要借助backgroundImage的repeat、backgroundPositionY等属性进行技术实现,开发环境是在VUE下,不过好在基本函数化了,并不涉及太多DOM结构,改造components function 成本也不会很大;

    UI

    • 先看下静态UI,以便于有个图像概念

    JS-VUE-老虎机抽奖

    方案分析

    • 一:参数配置,后期灵活调整

      入参,包括移动肚肚、圈数、参考值等
      
    • 二:初始化UI起始位置、动态设置背景

      1.动态设置背景,便于后期组件维护,避免css的改写
      2.组起始的展示位置   
      
    • 三.配置分组,每一条线需要定义结束位置、运行总线等

      三条单线,每条单线的配置类,并且运行的入口操作也在这里
      
    • 四.轨迹线路计算

      轨迹计算,递归模式下,每次运行的距离数组,即轨迹线
      
    • 五.轨迹运行

      requestAnimationFrame 帧动画开启
      

    初始参考各值参考图

    • 注,为了更好的适配与计算,以固定单位px作为计量,涉及各类机型,均以参考值进行动态计算,以及下文中大部分 “/2” 的操作均为2倍psd的处理;
    • 并以下运行奖项线条图,均简称为线图;

    JS-VUE-老虎机抽奖

    方案分析-参数配置

    这里我就不传入了,直接default模式;

    props: {
    img: {
      type: String,
      default:
        ''
    },
    rlc: {
      type: Object,
      default: () => {
        return {
          eh: 158 / 2,  // 每块区域从空白区域开始 - 单一模块的末尾结束
          ew: 119 / 2, // 参考宽度
          ih: 63 / 2, // 初始移动的高度 63 = (312/2 - 93)  312 = 容器高度,计算中间距离 93 = 第一个空白区域到第一卡片中间的位置
          ah: 2368 / 2, // 全图高度
          rfh: (158 - 63) / 2, // 起始基准值 第一个元素运行到中间的基础位置
          sp: 50, // 速度
          ln: 7, // 圈数
          dt: 750 // 默认多少市场后执行 下一轨迹
        }
      }
    }
    },
    

    方案分析-初始化UI起始位置

    • mounted:当线图有总高度传入时与非传入的自动读取设置

      mounted () {
      // 初始化入口高度
      var vm = this
      if (this.rlc.ah) {
        this.c_ah = this.rlc.ah
        this.initPx()
      } else {
        var img = new Image()
        img.src = this.img
        img.onload = function (e) {
          vm.c_ah = img.height / 2
          vm.initPx()
        }
      }
      },
        
      
    • initPx

      initPx () {
      // 自动设置定位高度
       window.addEventListener('DOMContentLoaded', e => {
        const ps = document.getElementById('tiger-wrap').getElementsByTagName('p')
        var width = null
        var newheight = null
        width = ps[0].clientWidth // 当前单个子容器实际宽度
        newheight = parseInt(width / (this.rlc.ew / this.c_ah)) // 比例计算 px适配机型
        this.initStart = parseInt((this.rlc.ih / this.c_ah) * newheight) // 根据参照下沉高度计算出真实高度
        this.ph = newheight // 精灵图总高度
        ps.forEach(ele => {
          ele.style.backgroundImage = `url(${this.img})`
          ele.style.backgroundSize = `100% ${newheight}px`
          ele.style.backgroundPositionY = `${this.initStart}px`
        })
       })
      }
        
      

    方案分析-配置分组

    • 得到每条线图的运行参数,并此函数作为对外的入口;

    • scale的计算,-2的操作是作为将第一个中奖单位定性为1,即运动距离减,运动距离为 -Y;

      start (drawArr, endBack) {
      // 配置处理区 完成各组配置
      // drawArr 入口传入的运动索引数组
      // endBack 开始运行轨迹回调
        
      var prevConfig = this.drawConfig
      this.drawConfig = []
      const reference = this.rlc.rfh
      const img1 = this.$refs.img1
      const img2 = this.$refs.img2
      const img3 = this.$refs.img3
      const domArr = [img1, img2, img3]
      drawArr.forEach((val, index) => {
        const scale = reference + this.rlc.eh * (val - 2) // 以第一个下沉作为参照 this.rlc.eh * (val - 2)完整的距离会多一个 reference
        const pp = prevConfig[index] ? Math.abs(prevConfig[index].endPoint) : 0
        this.drawConfig.push({
          endPoint: -parseInt((scale / this.c_ah) * this.ph), // 结束为止高度
          startPoint: (prevConfig[index] && prevConfig[index].endPoint) || 0, // 开始位置高度
          index: index, // 当前抽奖位置
          dom: domArr[index], // 当前dom
          turn: true, // 当前组是否抽奖开关
          stotal: -this.ph * this.rlc.ln - parseInt((scale / this.c_ah) * this.ph), // 理想情况下的参考 运动路线总长
          total: -this.ph * this.rlc.ln - parseInt((scale / this.c_ah) * this.ph) + pp // 兼容上一次停留位置 计算总线
        })
      })
      // 根据设定间隔开始运行
      this.drawConfig.forEach((val, index) => {
        setTimeout(() => {
          this.Rundom(val)
        }, index * this.rlc.dt)
      })
      this.endBack = endBack
      },
        
      

    方案分析-轨迹线路计算

    • 即得到运行数据组,简单理解,维护一个合理数组线,以减大数值加速,减小数值减速;

    • 结束的末尾数据串,应为像素1,以便于停止到指定位置,不产生偏移,本文定为10;

      Runline (domPage) {
      // 轨迹 每一条线对应一组轨迹运动
      const { total, startPoint, stotal } = domPage
      const lineArr = []
      const num = this.rlc.sp // 每次轨迹距离/运行速度
      let numwrap = 0
      let slow = 0
      let newnumwrap = 0
      const forlength = -parseInt(total / 2 / num)
      // 初始值的跑秒
      if (this.initStart > startPoint && startPoint === 0) {
        lineArr.push([this.initStart, 0, 3])
      }
      for (let i = 0; i < forlength; i++) {
        slow = i * 0.5 + 3
        lineArr.push([-i * num + startPoint, num * -i - num + startPoint, slow])
      }
      numwrap = lineArr[lineArr.length - 1][1] * 1
      for (let j = 0; j < forlength; j++) {
        lineArr.push([
          -j * num + numwrap,
          num * -j - num + numwrap,
          slow - j * 0.5 + 1
        ])
      }
      newnumwrap = lineArr[lineArr.length - 1][1]
      // 停止的间隔优化 初始化 第一次结束 起始位置不为 >= 0
      if (newnumwrap > total && total === stotal) {
        if (Math.abs(total) - Math.abs(newnumwrap) > 10) {
          const next2 =
            newnumwrap -
            (Math.abs(total) - Math.abs(newnumwrap) - 10)
          lineArr.push([newnumwrap, next2, 3])
        }
        lineArr.push([newnumwrap, total, 1])
      }
      // 起始位置不为 0
      // 此时需注意 每组跑完停留的位置并非0
      if (total > stotal) {
        if (Math.abs(stotal) - Math.abs(total) > 10) {
          const next2 =
            newnumwrap -
            (Math.abs(stotal) - Math.abs(newnumwrap) - 10)
          lineArr.push([newnumwrap, next2, 3])
        }
        lineArr.push([newnumwrap, stotal, 1])
      }
      return lineArr
      }, 
        
        
      

    方案分析-轨迹运行

    • 以上述,处理好的配置,进行跑数据,即循环数据组,--数据值

      Rundom (domPage) {
      // 开始运行轨迹
      var num = domPage.startPoint || this.initStart // 默认从 0 开始
      var lineConfig = this.Runline(domPage) // 拿到轨迹线
      var ph = this.ph
      // RunBody 轨迹执行器
      this.RunBody(() => {
        lineConfig.forEach(ele => {
          if (num > ele[1] && num <= ele[0]) {
            num = num - ele[2]
          }
        })
        if (num <= lineConfig[lineConfig.length - 1][1]) {
          this.drawConfig[domPage.index].turn = false
          if (!this.drawConfig[2].turn) {
            this.endBack && this.endBack()
          }
        }
        domPage.dom.style.backgroundPositionY = `${num}px`
        if (!this.drawConfig[domPage.index].turn) {
          domPage.dom.style.backgroundPositionY = `${
            this.drawConfig[domPage.index].endPoint
          }px`
        }
      }, domPage)
      },
      
    • 帧动画

      RunBody (into, domPage) {
      // 运动 requestAnimationFrame webkitRequestAnimationFrame
      // 帧动画处理方式 递归执行
      // 非兼容模式下 采用setTimeout
      if (!this.drawConfig[domPage.index].turn) return
      const RunBody = () => {
        into()
        this.RunBody(into, domPage)
      }
      if (window.requestAnimationFrame) {
        window.requestAnimationFrame(RunBody)
      } else if (window.webkitRequestAnimationFrame) {
        window.webkitRequestAnimationFrame(RunBody)
      } else {
        window.setTimeout(RunBody, 10)
      }
      },
        
        
      

    组件使用

    • 引入
    import tigerDraw from './components/tiger/tiger'
    <tigerDraw ref="tigerDraw" :img="img"></tigerDraw>
    
    • 调用
    this.$refs.tigerDraw.start([1, 2, 3], () => {})
    

    抽奖效果

    JS-VUE-老虎机抽奖

    结语

    以上就是大概的实现思路了,有些地方还是略显粗糙的;另外从整体的描述来看,一些兼容的参考数值还是有点绕的,还是需要结合源码去跑一下,不然可能没有太直接的代入感;到最后也是希望能抛砖引玉了,探讨下多种实现方案,对比分析一些优缺点。

    另附本文-源码地址,欢迎探讨哈~


    起源地下载网 » JS-VUE-老虎机抽奖

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元