Vue<幸运抽奖-老虎机🎰>

2021-01-28 11-47-11.gif

在线演示地址

没用canvas!没用canvas!没用canvas!你没听错,这里只要你css+html学的好,简单的 animation 动画,教你做老虎机!
下面我将分几个步骤,告诉大家如何实现的,想直接看代码的,可以直接看最下面的代码
此篇文章实现的原理和 Vue<幸运抽奖-大转盘> 比较类似,所以像更好的理解老虎机的实现原理,可以去参考下,这里针对细节方面就不做过多的解释了。

实现步骤:

1.布局实现
用到了 perspective和 transform-style: preserve-3d;旋转,或许直接看图就能明白个大概了,这样就能很好的实现根据奖品个数旋转成一个圆形,那么外层只需要一个标签包裹,设置一个 overflow: hidden 就🉑️~

:style="`transform: rotateX(${360/list.length*index}deg)  translateZ(${13.6*list.length}px)`"

image.png

2.动画转动
如:7个奖品,那么每个奖品都会有针对自己的class动画,及.wr1,.wr2,.wr3...,如果抽中的结果奖品是2,那么相当于每个列表的奖品都会去执行 .wr2的动画。

winner: 2, //指定的奖品 null时为不中奖
2021-01-28 12-11-55.gif

详细的实现原理可参考文章 Vue<幸运抽奖-大转盘> 实现步骤** 3** 🉐️介绍

这里会遇到一个问题,就是如果在连续抽奖时,下次抽奖开始的时候,还想接着当前的位置继续抽奖,该怎么搞?
这里提供了一个animationClass用来储存每次抽奖后的位置和class,方便下次转动时的动画,根据当前的位置进行转动,以及移除相应class添加新的class动画

 animationClass: [] //3个抽奖模块对应的动画属性,方便后来对应添加和移除该class样式

因为重置的位置在 360deg以内 所以不会影响css动画中5 * 360deg的起步值,因此每次还是会继续旋转起来

this.animationClass[index] = {
          num: winner,
          class: `wr${winner}`,
          rotate: (360 / this.list.length) * -winner
        }

使用参数:

list: [],//奖品列表,目前最多支持8个,想支持更多,可以手动添加对应的css样式动画
winner: null, //指定的奖品 null时为不中奖
loading: false, //抽奖执行状态,防止用户多次点击
gundongElement: null,//储存获取到的滚动容器的标签
animationClass: [] //3个抽奖模块对应的动画属性,方便后来对应添加和移除该class样式
$time:3s; //老虎机的转动时间 3s 后停止,修改后需要在计时器 setTimeout 中修改对应获奖提示的时间

代码如下:

<template>
  <div class="overall">
    <div>
      <div class="cj-box">
        <div class="cj-block" v-for="item in 3" :key="item">
          <ul class="gundong">
            <li
              v-for="(i,index) in list"
              :key="index"
              :style="`transform: rotateX(${360/list.length*index}deg)  translateZ(${13.6*list.length}px)`"
            >{{i.title}}</li>
          </ul>
        </div>
      </div>
      <div class="chou" @click="start">抽奖</div>
    </div>
  </div>
</template>

<script>
export default {
  computed: {},
  data() {
    return {
      list: [
        {
          title: '0'
        },
        {
          title: '1'
        },
        {
          title: '2'
        },
        {
          title: '3'
        },
        {
          title: '4'
        },
        {
          title: '5'
        },
        {
          title: '6'
        },
        {
          title: '7'
        }
      ], //奖品列表,目前最多支持7个,想支持更多,可以手动添加对应的css样式动画
      winner: null, //指定的奖品 null时为不中奖
      loading: false, //抽奖执行状态,防止用户多次点击
      gundongElement: null, //储存获取到的滚动容器的标签
      animationClass: [] //3个抽奖模块对应的动画属性,方便后来对应添加和移除该class样式
    }
  },
  mounted() {
    //通过获取奖品个数,来改变css样式中每个奖品动画的旋转角度
    // var(--nums) 实现css动画根据奖品个数,动态改变
    let root = document.querySelector(':root')
    root.style.setProperty('--nums', this.list.length)
  },
  methods: {
    //开始抽奖
    start() {
      if (!this.loading) {
        this.gundongElement = document.querySelectorAll('.gundong')
        this.gundongElement.forEach((element, index) => {
          if (this.animationClass[index]) {
            element.classList.remove(this.animationClass[index].class)
          }
        })
        //此处可指定后端返回的指定奖品------------------------------------------------
        // this.winner = this.winner
        this.winCallback()
        this.loading = true
      }
    },
    //中奖返回方法
    winCallback() {
      setTimeout(() => {
        /* 此处是为了解决当下次抽中的奖励与这次相同,动画不重新执行的 */
        /* 添加一个定时器,是为了解决动画属性的替换效果,实现动画的重新执行 */
        this.gundongElement.forEach((element, index) => {
          element.classList.add(this.getAnimationClass(this.winner, index))
          setTimeout(() => {
            element.style.transform = `rotateX(${this.animationClass[index].rotate}deg)`
          }, 2000)
        })
      }, 0)
      // 因为动画时间为 3s ,所以这里4s后获取结果,其实结果早就定下了,只是何时显示,告诉用户
      setTimeout(() => {
        this.loading = false
        console.log(
          `恭喜你获得了${this.winner}-----------------------------------`
        )
      }, 4000)
    },
    //获取对应的class样式,并储存起来,方便后来的class移除
    getAnimationClass(winner, index) {
      //如果是后端指定奖品
      if (winner !== null) {
        this.animationClass[index] = {
          num: winner,
          class: `wr${winner}`,
          rotate: (360 / this.list.length) * -winner
        }
        //对应css样式中定义的class属性值,如果有更多的话可以继续添加  case 8:   return 'wr8'
        return `wr${winner}`
      } else {
        /* 此处是当不中奖时,那么3个轮盘会自动随机 */
        let ran = this.random(0, this.list.length - 1)
        /* 此处是为了避免随机时的3个数字一样,因为不中奖的时候3个数字是不可能一样的 */
        if (this.animationClass[0]) {
          while (this.animationClass[0].num === ran) {
            ran = this.random(0, this.list.length - 1)
          }
        }
        this.animationClass[index] = {
          num: ran,
          class: `wr${ran}`,
          rotate: (360 / this.list.length) * -ran
        }
        //对应css样式中定义的class属性值,如果有更多的话可以继续添加  case 8:   return 'wr8'
        return `wr${ran}`
      }
    },
    //随机一个整数的方法
    random(min, max) {
      return parseInt(Math.random() * (max - min + 1) + min)
    }
  }
}
</script>

<style lang="scss" scoped>
$time: 3s; //老虎机的转动时间 3s 后停止,修改后需要在计时器 setTimeout 中修改对应获奖提示的时间
.overall {
  perspective: 3000px;
}
/* 抽奖按钮 */
.chou {
  user-select: none;
  cursor: pointer;
  padding: 10px;
  margin: 20px auto;
  color: white;
  background: #19aca4;
  width: 100px;
  font-size: 20px;
  border-radius: 5px;
  box-shadow: 0 0 10px #ccc;
  &:active {
    transform: scale(0.9);
  }
}
.cj-box {
  height: 100px;
  background: white;
  box-shadow: 0 0 10px #ccc;
  border-radius: 5px;
  padding: 20px 40px;
  display: flex;
  .cj-block {
    width: 100px;
    overflow: hidden;
    box-sizing: border-box;
    margin: 0 1px;
    .gundong {
      display: inline-block;
      transform-style: preserve-3d;
      width: 100px;
      height: 100%;
      list-style: none;
      padding: 0;
      position: relative;
      perspective: 3000px;
      /* 每个奖品格子的样式 */
      li {
        transform-style: preserve-3d;
        // transform-origin: 50% 0%;
        display: block;
        height: 100px;
        line-height: 100px;
        font-weight: bold;
        font-size: 30px;
        position: absolute;
        width: 100%;
        height: 100%;
        color: #fcfcfc;
      }
    }
  }
}
/* 给每个滚动添加一个延迟动画效果 */
.cj-block:nth-child(1) ul {
  animation-delay: 0.2s;
  li {
    background: #14e4b7;
  }
}
.cj-block:nth-child(2) ul {
  animation-delay: 0.5s;
  li {
    background: #14dde4;
  }
}
.cj-block:nth-child(3) ul {
  animation-delay: 0.8s;
  li {
    background: #14e4b7;
  }
}
/* 下面的css样式为每个奖品的旋转动画,这里写了对应8个奖品的动画,如果想要更多的话,可以添加 */
/* 例如: .wr8  @keyframes play8 */
.wr0,
.wr1,
.wr2,
.wr3,
.wr4,
.wr5,
.wr6,
.wr7 {
  /* 每个转动的时间 */
  animation-duration: $time;
  animation-timing-function: ease;
  animation-fill-mode: both;
  animation-iteration-count: 1;
}
.wr0 {
  animation-name: play0;
}
.wr1 {
  animation-name: play1;
}
.wr2 {
  animation-name: play2;
}
.wr3 {
  animation-name: play3;
}
.wr4 {
  animation-name: play4;
}
.wr5 {
  animation-name: play5;
}
.wr6 {
  animation-name: play6;
}
.wr7 {
  animation-name: play7;
}
@keyframes play0 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * 0));
  }
}
@keyframes play1 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * -1));
  }
}
@keyframes play2 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * -2));
  }
}
@keyframes play3 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * -3));
  }
}
@keyframes play4 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * -4));
  }
}
@keyframes play5 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * -5));
  }
}
@keyframes play6 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * -6));
  }
}
@keyframes play7 {
  to {
    transform: rotateX(calc(5 * 360deg + 360deg / var(--nums) * -7));
  }
}
</style>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容