抽奖动画 - 滚动抽奖

1. 需求

上次讲到lao虎机抽奖,通过jquery.animate修改background-prosition-y来修改背景图片实现上下滚动动画,本文介绍一种左右滚动的动画,原理是一样的,这里通过修改background-prosition-x来实现动画,同时也要注意奖品图片的尺寸问题,这里姑且叫它滚动抽奖。先看高保,如下图1:
image
图1
需求如下:

  1. 奖品图片横排列。
  2. 用户点击抽奖,奖品依次向左滚动,先空转2圈。
  3. 请求抽奖接口,待接口有返回后,根据接口返回的结果固定在某个奖品上。

注意这里每个奖品图片的宽度要是固定的,否则在计算奖品图片位置的时候对不准,这个要和UI设计人员事先约定,做好沟通。

2. 整体思路

2.1 奖品顺序

为了最后选中奖的奖品,要按照图片的顺序定义奖品数据的顺序,最后使用奖品数组的index来计算滚动位置并播放第二个动画,所以定义奖品数组的时候要按照奖品的背景图片中的殊勋保持一致,方便动画定位。

2.2 background-prosition-x

background-position为每一个背景图片设置初始位置。 这个位置是相对于由background-origin定义的位置图层的。background-origin有三种取值,如下:

  1. border-box
    背景图片以border区域为参考,默认值
  2. padding-box
    背景图片以padding区域为参考
  3. content-box
    背景区域以content区域为参考

background-position定义一对x/y坐标,来放置背景图片,它可以被定义成一个值或两个值,如果被定义成两个值,第一个值代表水平位置,第二个代表垂直位置。如果只指定一个值,则第二个值为center。
也可以单独设置x坐标或y坐标,例如background-position-x用来设置背景图片水平方向的位置。
background-position-x为0时,背景图片与容器左边界对齐,如果background-position-x为20px时,图片相对容器向右偏移20px,前面20px是空白,看上去是一个放在容器中间的图片。如下图2
image
图1
相反,如果background-position-x为-20px时,背景图片从向左偏移20px,图片有20px被遮盖了,如下图3
image
图3
使用jQuery.animate函数来不断修改background-prosition-x,并且设置repeat,这样看起来就是图片在滚动了,这个抽奖动画的原理就是这样。

2.3 定位奖品

最后请求接口,通过奖品属性prizeId找到这个奖品在数组中的下标index,根据index和图片宽度计算background-position-x。

3. 实现过程

3.1 布局

UI提供了背景图片和奖品图片,这里主要使用relative,absolute定位的方式来布局。如下图4首先使真个抽奖块的背景图。注意从高保中可以看到整个空白的框中的长度是2个奖品图片的长度,这个在后后面给奖品图片定位的时候会用到。
image
图3
然后再把奖品图片作为背景图片放在这个框中。这个奖品图片如下图4
image
图4
放在抽奖框中的效果如下图5
image
图4
然后修饰一下,在两边用阴影,让它看起来像有一个弧度,类似手机上的曲面屏,如下图5。
image
图5
最后给加上抽奖按钮和指针图片,效果如下图6
image
图6

3.2 奖品顺序

上图4中UI给出了奖品顺序,那定义奖品数组的时候就要按照这个顺序来了。注意奖品图片中“谢谢合作”和“无线耳机”重复出现了2次,这是为了使背景滚动起来看上去连贯一点,没有什么用,实际奖品是从“无线耳机”开始到“谢谢参与”结束,定义数据如下:

prizeList: [
  {prizeId: '100860911', index: 0, prizeName: 'AirPods2代无线耳机'},
  {prizeId: '100860910', index: 1, prizeName: '20元优惠'},
  {prizeId: '100860909', index: 2, prizeName: '10元优惠'},
  {prizeId: '100860908', index: 3, prizeName: '5元优惠券'},
  {prizeId: '100860907', index: 4, prizeName: '2元现金加赠券'},
  {prizeId: '100860904', index: 5, prizeName: '1元优惠券'},
  {prizeId: '100860903', index: 6, prizeName: '1元优惠券'},
  {prizeId: '100860902', index: 7, prizeName: '5元满减券'},
  {prizeId: '100860901', index: 8, prizeName: '2元满减券'},
  {prizeId: '100860906', index: 9, prizeName: '0.5元现金券'},
  {prizeId: '100860905', index: 10, prizeName: '0.2元现金券'},
  {prizeId: '100860900', index: 11, prizeName: '谢谢参与'}
]

3.3 初始位置

奖品图片的初始位置要落在第一个奖品“无线耳机”,要根据奖品图片的大小来计算。定义两个变量来记住图片大小。

let totalLth = 3962               //奖品图片总长度
let itemLth = totalLth / 14       //每个奖品长度

奖品图片的初始位置是整个图片的长度除以14(一共14个小图片)除以75(postcss-plugin-px2rem中定义的rootValue),再乘以0.5得到半个奖品图片的长度,单位是rem,要让图片向左移动,所以要取负值,这样正好向左移动半个图片长度,使第一个奖品正好落在指针正中央。定义变量记住这个初始偏移尺寸,如下:

locationX: -1.8866666666666667              //3962/14/75*0.5

定位结果如下图7
image
图7
这半个图片长度是根据整个框占2个图片宽度,奖品在小图片正中间,第一个奖品图片前面有个“谢谢参与”,在整个背景的1.5个图片宽度处,所以2-1.5=0.5,最后正好落在指针处,就是正中间。

3.4 jQuery动画

jQuery动画和可以很方便的修改DOM元素的尺寸信息,包含background-position信息,参考jquery文档如下

Animation Properties and Values(动画属性和值)
所有用于动画的属性必须是数字的,除非另有说明;这些属性如果不是数字的将不能使用基本的jQuery功能。(例如,width, height或者left可以执行动画,但是background-color不能,除非使用jQuery.Color()插件。)属性值的单位像素(px),除非另有说明。单位em 和 %需要指定使用。

另外jQuery的动画属性也可以是一个相对值,这样我们从接口响应中获取到奖品信息后,就可以非常方便的播放第二个动画,参考jQuery文档如下:

动画属性也可以是一个相对值。如果提供一个以+= 或 -=开始的值,那么目标值就是以这个属性的当前值加上或者减去给定的数字来计算的。

最后jQuery.animate动画提供complete回调函数指定动画完成之后要处理的事情,也可以用来按照顺序播放动画,参考jQuery文档如下:

Complete Function(完成函数)
如果提供complete回调函数将在动画完成后被执行一次。这对于按顺序执行一系列不同动画时,特别有用。这个回调函数不设置任何参数,但是this会被传递到正在执行动画的DOM元素上,如果多个元素一起做动画效果,值得注意的是这个回调函数在每个匹配元素上执行一次,不是这个动画作为一个整体。

3.5 空转

布局有了,动画思路也有了,现在可以让背景动起来了。先让整个背景转2圈,模拟开奖,代码如下:

drawLottery() {
  let totalLth = 3962               //奖品图片总长度
  let itemLth = totalLth / 14       //每个奖品长度
  let wheel = 2                     //空转2圈
  //先空转
  window.jQuery('#slotMachine').css('background-position-x', `${this.locationX}rem`)
  window.jQuery("#slotMachine").animate(
    {backgroundPositionX: `-=${((totalLth * wheel) / 75)}rem`},
    {duration: 5000, easing: 'easeInOutQuint'}
  )
}

注意每次开始播放动画之前先让背景回到第一个奖品“无线耳机”上,不然的话连续抽检的话就对不准了。效果如下图8:

图8
从动画中可以看到,让背景滚动了2圈,2*整个背景的长度,最后正好回到原点。但是为了方便我们最后计算奖品位置,要让背景回到background-position-x: 0的位置,这样最后计算的时候可以直接用index来计算,便于理解。这里只要在backgroundPositionX基础上减去半个图片尺寸就好,如下:
image
图9

3.6 请求接口&第二个动画

第一个动画空转,设置的时间是5秒,动画播放完就要定位奖品了,这里和后端约定好,抽奖接口5秒内一定要返回结果,否则第二个动画就无法定位。
最后使用Complete Function来处理接口返回的结果。代码如下:

window.jQuery("#slotMachine").animate(
  {backgroundPositionX: `-=${((totalLth * wheel - (itemLth / 2)) / 75)}rem`},
  {duration: 5000, easing: 'easeInOutQuint', complete: function() {
    getPrizeInfo(window.jQuery(this))
  }}
)

注意在complete回调函数中this是正在执行动画的DOM元素,正好可以用来用这个DOM播放第二个动画。这里complete不能使用箭头函数,否则this会指向当前vue组件。
getPrizeInfo()方法处理接口结果,并播放动画,代码如下:

//抽奖
let getPrizeInfo = slotMachine => {
  let chooseIndex = this.getRandomIntInclusive(0, this.prizeList.length - 1)
  console.log(JSON.stringify(this.prizeList[chooseIndex]))
  slotMachine.animate(
    {backgroundPositionX: `-=${((chooseIndex + 0.5) * itemLth / 75)}rem`},
    {duration: (chooseIndex + 1) * 800, easing: 'swing'}
  )
}

这里我们用一个随机函数取出奖品结果。代码如下:

//返回随机数,大于等于min,小于等于max
getRandomIntInclusive(min, max) {
  min = Math.ceil(min)
  max = Math.floor(max)
  return Math.floor(Math.random() * (max - min + 1)) + min //含最大值,含最小值 
}

注意chooseIndex+0.5也是为了调整奖品图片的对应位置。最后动画效果如下图10
image
图10

4. 总结

本文介绍了另外一种方式的抽奖动画,和lao虎机很类似,只不过是左右滚动,原理依然是使用jQuery.animate修改background-position-x属性来实现滚动。

posted @ 2022-08-12 14:14  nd  阅读(984)  评论(0编辑  收藏  举报