大致樣式如下:

一、構(gòu)思
獎勵物品是通過接口獲取的(img)
獎勵結(jié)果是通過接口獲取的(id)
抽獎的動畫需要由慢到快再到慢
抽獎轉(zhuǎn)動時間不能太短
抽獎結(jié)束需要回調(diào)
業(yè)務代碼和功能代碼要分離
二、先完成一個 UI
使用 flex 來布局,當?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;
? ? ? ? ? ? }
? ? ? ? }
? ? }
效果圖:

三、開始做動畫效果
新建一個?Game?的?class,有有個?run?方法和?finish?方法
開始運行
動畫的速度是變化的,使用?requestAnimationFrame?和?setInterval?有點不妥,所以:可以使用?setTimeout?+?speed 參數(shù)?來控制動畫的速度。
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);
? ? }
}
結(jié)束運行
收到結(jié)束運行的通知時,需要先做減速動畫,然后再停止在對應的?num,然后調(diào)用回調(diào)函數(shù),所以先暫存結(jié)束回調(diào)和結(jié)束點,并將動畫設(shè)置為減速。
finish(num, finishCb) {
? ? ? ? this.oil = false;
? ? ? ? this.endIdx = num;
? ? ? ? this.finishCb = finishCb;
? ? }
速度的控制
1、默認速度為加速(this.oil = true)通過是否達到預期速度來停止加速,當減速時同理。
2、為達到緩動結(jié)束效果,所以結(jié)束時間通過:到達最小速度 且 到達結(jié)束位置。
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?展示的,而動畫的執(zhí)行是轉(zhuǎn)圈,所以需要矯正?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];
? ? }
活動代碼與業(yè)務代碼互動
將需要交互的函數(shù)傳遞給?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;
最后:樣式比較粗略,主要還是實現(xiàn)功能