要完成一个这样的抽奖功能

构思

  1. 奖励物品是通过接口获取的(img)
  2. 奖励结果是通过接口获取的(id)
  3. 抽奖的动画需要由慢到快再到慢
  4. 抽奖转动时间不能太短
  5. 抽奖结束需要回调
  6. 业务代码和功能代码要分离

先完成一个 UI

使用 flex 来布局,easy,当 curGameIdx 等于当前奖品 index 时高亮

html

    <div class="game-box">
<template
v-for="(val, idx) of boundList">
<div v-if="idx == 4" class="game-item game-begin"
:key="idx"
@click="beginGame">
开始游戏
</div>
<div v-else :key="idx"
class="game-item"
:class="{
active: idx === curGameIdx
}">
{{val}}
</div>
</template>
</div>

css

    .game-box {
display: flex;
flex-wrap: wrap;
text-align: center;
.game-item {
width: 1.25rem;
height: 0.3rem;
background: yellow;
border: 1px solid transparent;
transition: all 0.2s;
&.game-begin {
background: transparent;
}
&.active {
border: 1px solid black;
}
}
}

效果图

开始做动画效果

新建一个 Gameclass,有有个 run 方法和 finish 方法

开始运行

动画的速度是变化的,使用 requestAnimationFramesetInterval 有点不妥,所以:可以使用 setTimeout + speed 参数 来控制动画的速度。

class Game {
constructor(idx) {
this.idx = idx;
this.speed = 400;
} addIdx(){
} speedControl() {
} finish() {
} run(cb) {
this.speedControl();
setTimeout(() => {
this.addIdx();
!this.isFinish && this.run(cb);
}, this.speed);
}
}

结束运行

收到结束运行的通知时,需要先做减速动画,然后再停止在对应的 num,然后调用回调函数,所以先暂存结束回调和结束点,并将动画设置为减速。

    finish(num, finishCb) {
this.oil = false;
this.endIdx = num;
this.finishCb = finishCb;
}

速度的控制

  1. 默认速度为加速(this.oil = true)通过是否达到预期速度来停止加速,当减速时同理。
  2. 为达到缓动结束效果,所以结束时间通过:到达最小速度 且 到达结束位置。
    speedUp() {
this.speed -= 60;
} speedDown() {
this.speed += 200;
} speedControl() {
if (this.speed > this.Max_Speed) {
if (this.oil) {
this.speedUp();
}
}
if (!this.oil) {
if (this.speed < this.Min_Speed) {
this.speedDown();
} else if (this.endIdx === this.idx) {
this.isFinish = true;
typeof this.finishCb === 'function' && this.finishCb();
}
}
}

index 矫正

此时,上面 UI 是通过 v-for + flex 展示的,而动画的执行是转圈,所以需要矫正 index

更改上面 addIdx 方法,矫正 index,并将 ++index 取余

    constructor(idx) {
this.idx = idx;
this.speed = 400;
this.order = null;
this.Order_List = [0,1,2,5,8,7,6,3];
this.Game_Box_Num = 8;
} addIdx() {
this.idx = (++this.idx % this.Game_Box_Num);
this.order = this.Order_List[this.idx];
}

活动代码与业务代码互动

将需要交互的函数传递给 Game 的实例即可

  // vue 代码
methods: {
updateGameIdx(order) {
this.curGameIdx = order;
},
gameFinish() {
this.playing = false;
console.log(this.curGameIdx, 'curGameIdx')
},
beginGame() {
if (this.playing) return;
this.playing = true;
this.curGameIdx = 0;
const game = new Game(this.curGameIdx);
game.run(this.updateGameIdx);
// 通过请求终止
setTimeout(() => {
game.finish(2, this.gameFinish)
}, 3000);
}
}

最后附上完整 Game 代码:

class Game {
constructor(idx) {
this.idx = idx;
this.speed = 400;
this.oil = true;
this.isFinish = false;
this.endIdx = null;
this.finishCb = function() {}
// 常量
this.Max_Speed = 100;
this.Min_Speed = 500;
this.Order_List = [0,1,2,5,8,7,6,3];
this.Game_Box_Num = 8;
} speedUp() {
this.speed -= 60;
} speedDown() {
this.speed += 200;
} speedControl() {
if (this.speed > this.Max_Speed) {
if (this.oil) {
this.speedUp();
}
}
if (!this.oil) {
if (this.speed < this.Min_Speed) {
this.speedDown();
} else if (this.endIdx === this.idx) {
this.isFinish = true;
typeof this.finishCb === 'function' && this.finishCb();
}
}
} finish(num, finishCb) {
this.oil = false;
this.endIdx = num;
this.finishCb = finishCb;
} addIdx() {
this.idx = (++this.idx % this.Game_Box_Num);
} run(cb) {
this.speedControl();
typeof cb === 'function' && cb(this.Order_List[this.idx]);
setTimeout(() => {
this.addIdx();
!this.isFinish && this.run(cb);
}, this.speed);
}
} export default Game;

大致效果

主要功能已经实现,想漂亮点再改改 CSS 就好了,动画时间也需要再调试。(避嫌,具体结果不能提供 - -。)

最后

译者写了一个 React + Hooks 的 UI 库,方便大家学习和使用,

React + Hooks 项目实战

欢迎关注公众号「前端进阶课」认真学前端,一起进阶。

最新文章

  1. WPF DataGrid绑定及列居中
  2. iOS9新特性(1)-解决http请求失败的问题
  3. Uva10635 LCS
  4. 错误代码:ERR_UNSAFE_PORT
  5. Can&#39;t update table &#39;test_trigger&#39; in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
  6. python-面向对象(四)——类成员的访问方式汇总
  7. stm32 ARM中的RO、RW和ZI DATA
  8. storm 使用过程中遇到的问题
  9. C++调用外部应用程序的方法的整理总结(常用)
  10. devexpress表格控件gridcontrol图片列,按钮列,时间列等特殊列的实现
  11. 关于SqlServer远程跨库修改数据
  12. spark streaming(2) DAG静态定义及DStream,DStreamGraph
  13. 前端的UI设计与交互之布局篇
  14. Linux下文件权限(一)用户ID和用户组ID
  15. 手写JAVA虚拟机(三)——搜索class文件并读出内容
  16. php载入脚本的几种方式对比
  17. leetcode5:最长回文子串
  18. Windows任务计划 &amp; Linux crontab定时自动任务
  19. 安卓 dex 通用脱壳技术研究(三)
  20. 解决opencv和mfc同时使用导致memory leak

热门文章

  1. @codeforces - 1187F@ Expected Square Beauty
  2. Android教程 -08 ToolBar的使用和主题的介绍
  3. python unittest 框架添加测试用例及运行
  4. 使用Laravel的队列实现系统通知、
  5. git分支合并及冲突解决
  6. Spring Data Jpa一对多单向映射
  7. HTML DOM clearInterval() 方法
  8. [转]在Windows中安装Memcached
  9. Python--day46--用户管理设计方案介绍
  10. 【React】react项目引入echarts插件 K线图