1. 需求

开宝箱在一些商家营销活动中也很常见,这个需求简单,产品经理说就是做个动画就好,那我们就可以自由发挥了。高保图如下图1.
抽奖动画 – 开宝箱抽奖-小白菜博客
图1
开局一张图,剩下的全靠自己编了,动作怎么动,先看看UI给的素材,看米下锅。一张关闭的箱子图片,如下图2
抽奖动画 – 开宝箱抽奖-小白菜博客
图2
一张打开的箱子,如下图3
抽奖动画 – 开宝箱抽奖-小白菜博客
图3
一张奖品背景图片,是光芒,可以作为奖品的背景图,如下图4
抽奖动画 – 开宝箱抽奖-小白菜博客
图4

虽然需求文档中没有给出这个动画的具体需求,但可以根据产品的描述,可以把需求归纳一下,如下:

  1. 点击“立即开启”,先显示半透明的蒙版,再显示关闭的箱子,并且从小变大
  2. 请求接口,同时从关闭变成打开的箱子,在打开箱子的空白处显示中奖结果
  3. 用光芒图片作为奖品图片的背景,并且光芒不断地旋转起来
  4. 点击关闭,关闭整个中奖弹框

2. 思路

这个动画相对简单,可以直接使用css中的animation动画,通过keyframe定义动画帧来让图片从小变大,同时切换上面的图2和图3可以让宝箱状态从关闭变成打开,然后显示奖品就有了。然后还有一个地方,就是奖品的背景是一个圆形光芒,可以让它旋转,或者不断地变大变小,这样看起来更加的生动。

3. 实现过程

3.1 蒙版

点击按钮首先弹出蒙版,这就没啥好说的了,使用fix定位 + background + opacity可以做一个简单的蒙版,代码如下:

<!-- 抽奖按钮 -->
<img class="treasure" src="./../assets/images/treasurebox.gif" @click="openBox" alt=""/>
<!-- 弹出层 -->
<div v-if="showBox" class="pop-up">
<div class="prize-box"></div>

css代码如下:

.pop-up {
  position: fixed;
  z-index: 4;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.4);
}

注意上面代码中使用rgba作为背景色,最后一个参数是opacity,规定不透明度。从 0.0 (完全透明)到 1.0(完全不透明)。

3.2 开宝箱

宝箱图片有2个,一个是关闭的,一个是打开的,要用一个动画控制二者切换,并且还要掌握好他们的显示时间这样看起来是一个打开的过程,不过这里只有两张图片,无法看到打开过程,只能稍微模拟一下。如果要有整个过程的话,就要提供很多个中间状态的宝箱图片了。html代码如下:

<div v-if="showBox" class="pop-up">
  <div class="prize-box">
  </div>
</div>

css代码如下:

$aniDuration: 2s;
.prize-box {
  position: relative;
  width: 548px;
  height: 739px;
  margin: 81px auto auto auto;
  animation: openBox $aniDuration ease-out 1s 1 forwards;
  background: top/5% no-repeat url("./../assets/images/box@2x.png");
}
@keyframes openBox {
  from {
      background-position: center;
      -webkit-background-size: calc(548px * 0.1) calc(739px * 0.1);
      background-size: 5%;
      width: calc(548px * 0.1);
      height: calc(739px * 0.1);
      background-image: url("./../assets/images/box@2x.png");
      background-position: top;
  }
  50% {
      background-position: center;
      background-size: 50%;
      width: calc(548px * 0.5);
      height: calc(739px * 0.5);
      background-image: url("./../assets/images/box@2x.png");
      background-position: top;
  }
  80% {
      background-position: center;
      background-size: 110%;
      width: calc(548px * 1.1);
      height: calc(739px * 1.1);
      background-position: top;
  }
  to {
      background-position: center;
      background-size: 548px 739px;
      width: 548px;
      height: 739px;
      background-image: url("./../assets/images/boxopen@2x.png");
      background-position: top;
  }
}

代码中给class为prize-box的容器定义了一个动画openBox,用变量$aniDuration纪录动画持续时长,1秒后开始执行,最后停留在终态,时间函数(animation-timing-function)使用easy-out。在keyframe关键帧中修改了背景图片大小,dom容器大小等css属性,都是从小到大,并且在动画的0%80%部分是显示关闭宝箱,最后80%100%显示开启的宝箱。在动画的80%处背景稍微变大一下,看起来有膨胀的效果。可以想想一下,这个动画的整个过程是,关闭宝箱从小到大,变成开启的宝箱,并且膨胀一下,最后恢复到原始大小。

animation的第三个属性animation-timing-function是一个贝塞尔时间函数,可用的取值如下:

  1. linear
    动画以恒定速度运行。此关键词表示缓冲函数 cubic-bezier(0.0, 0.0, 1.0, 1.0)。如下图5。
    抽奖动画 – 开宝箱抽奖-小白菜博客
    图5
  2. ease
    动画缓慢开始,然后突然加速,最后缓慢移向目标。此关键词表示缓冲函数cubic-bezier(0.25, 0.1, 0.25, 1.0)。它与 ease-in-out 类似,但它在开始时加速更快。如下图6。
    抽奖动画 – 开宝箱抽奖-小白菜博客
    图6
  3. ease-in
    动画缓慢开始,然后逐渐加速直到结束,在结束点时突然停止。此关键词表示缓冲函数 cubic-bezier(0.42, 0.0, 1.0, 1.0),如下图7。
    抽奖动画 – 开宝箱抽奖-小白菜博客
    图7
  4. ease-in-out
    动画缓慢开始,然后加速,最后减速直至结束。此关键词表示缓冲函数 cubic-bezier(0.42, 0.0, 0.58, 1.0)。开始时,其表现与 ease-in 函数类似;结束时,与 ease-out 函数类似。如下图8。
    抽奖动画 – 开宝箱抽奖-小白菜博客
    图8
  5. ease-out
    此动画突然开始,然后逐渐减速直至结束。此关键词表示缓冲函数 cubic-bezier(0.0, 0.0, 0.58, 1.0)。如下图9。
    抽奖动画 – 开宝箱抽奖-小白菜博客
    图9

贝塞尔时间函数的内容很多,并且很晦涩,这里不再展开讨论。看看效果,如下图10
抽奖动画 – 开宝箱抽奖-小白菜博客
图10

3.3 请求接口

请求接口前需要定义好奖品,以便从接口中取出中奖奖品信息,代码如下:

data() {
  return {
    showBox: false,
    showPrize: false,
    prizeList: [
      {prizeId: "100860801", prizeName: "100元满减券"},
      {prizeId: "100860802", prizeName: "20元抵扣劵",},
      {prizeId: "100860803", prizeName: "10元抵扣券"},
      {prizeId: "100860804", prizeName: "1个月优惠员"},
      {prizeId: "100860805", prizeName: "1元红包"},
      {prizeId: "100860806", prizeName: "5红包"},
      {prizeId: "100860807", prizeName: "1元抵扣券"},
      {prizeId: "100860808", prizeName: "500元满减券"},
      {prizeId: "100860809", prizeName: "200元满减券"}
    ],
    title: ['发送给好友', '可增加一次开宝箱机会'],
    actionBtn: {id: 1, title: '呼朋唤友,再开一次'},
    prizeData: {prizeId: "100860801", prizeName: "100元优惠券"},      //抽奖信息奖信息
  }
}

这里依然使用getRandomIntInclusive()方法从奖品数据中随机取出一个奖品,模拟抽奖过程。代码如下:

/* 开宝箱抽奖 */
openBox() {
  this.showBox = true
  setTimeout(() =>  {
    this.showPrize = true
    let index = this.getRandomIntInclusive(0, this.prizeList.length - 1)
    this.prizeData = this.prizeList[index]
  }, 3000)
},
/* 关闭按钮 */
followAction() {
  this.showBox = false
  this.showPrize = false
},
//返回随机数,大于等于min,小于等于max
getRandomIntInclusive(min, max) {
  min = Math.ceil(min)
  max = Math.floor(max)
  return Math.floor(Math.random() * (max - min + 1)) + min //含最大值,含最小值 
}

3.4 显示奖品

开宝箱的动画有了,剩下的就是请求接口,显示奖品了。注意开启的宝箱有两处留白,这里要使用相对定位来固定奖品图片,奖品名称文案,抽奖提示文案,如下图11
抽奖动画 – 开宝箱抽奖-小白菜博客
图11

html代码如下:

<!-- 弹出层 -->
<div v-if="showBox" class="pop-up">
  <div class="prize-box">
    <template v-if="showPrize">
      <div class="prize">
        <img :src="require('./../assets/images/prize/' + prizeData.prizeId + '.png')" alt="" class="img-prize"/>
      </div>
      <div class="prizetip">
        <div>恭喜获您获得{{ prizeData.prizeName }}</div>
      </div>
      <div class="prizelead">
        <div class="content" @click="followAction">
          {{ actionBtn.title }}&gt;
        </div>
      </div>
    </template>
  </div>
  <img v-if="showPrize" class="close" src="./../assets/images/closebox@2x.png" @click="followAction" alt=""/>
</div>

css代码如下:

// 奖品
.prize {
  z-index: 5;
  position: absolute;
  left: 50%;
  margin-left: -118px;
  top: 190px;
  left: 50%;
  width: 219px;
  height: 219px;
  background-size: 219px 219px;
  background-position: center;
  align-content: center;
  display: flex;
  align-items: center;
  justify-content: center;
  .img-prize {
      width: 180px;
      height: 150px;
      z-index: 6;
  }
}
.prize:before {
  content: "";
  position: absolute;
  z-index: 4;
  width: 110%;
  height: 110%;
  background: center / contain no-repeat url("../assets/images/prizebg@2x.png");
  animation: turn 6s linear infinite;
}
@keyframes turn{
  0% { -webkit-transform: rotate(0deg); }
  100%{ -webkit-transform: rotate(360deg); }
}
.prizetip {
  z-index: 6;
  position: relative;
  top: 380px;
  margin: 0 auto;
  font-size: 28px;
  font-family: HuipuMR;
  color: rgba(51, 51, 51, 1);
  height: 72px;
  @include flex(center, center, column, nowrap);
  div {
      height: 40px;
      line-height: 40px;
  }
}
.prizelead {
  position: relative;
  top: 525px;
  margin: 0 auto;
  max-width: 374px;
  height: 70px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(90deg, rgba(247, 231, 165, 1) 0%, rgba(255, 234, 114, 1) 100%);
  box-shadow: 0px 4px 8px 0px rgba(203, 68, 15, 1);
  border-radius: 40px;
  .content {
    text-align: center;
    font-size: 28px;
    color: rgba(191, 62, 11, 1);
    line-height: 38px;
    text-shadow: 0px 4px 8px rgba(203, 68, 15, 1);
  }
}

注意,显示了奖品之后我们还在奖品的背景,光芒四射图片上设置了一个旋转动画,这个相对简单,就是设置rotate从0deg到360deg。并且把这个背景使用了before伪类,这样就不用再弄一个dom层了。

在最后加了一个关闭按钮,这个不再赘述。

看下效果,如下图12
抽奖动画 – 开宝箱抽奖-小白菜博客
图12

4. 总结

本文介绍了如何使用css的animation动画实现一个开宝箱抽奖的功能,主要就是animation+keyframe,相对简单。