前言
周末去参加好哥们的婚礼了,今天才赶回来,所以更新晚了。现在继续把九宫格抽奖组件后面的两个模块:动画以及事件写完。
动画
说到 js 动画,我首先想到的是性能最好的requestAnimationFrame,可惜微信小程序不支持,没办法,就只能“曲线救国”了。再者由于setInterval的坑比较多也被我排除了,最后我选择了setTimeout。
先说下跳动的实现:一段时间后,切换格子的激活状态到下一个格子,连贯起来就有动画的效果了。有了基础动画的实现思路,接下来就可以着手干活了:
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
const MAX_DELAY = 300; // 最大跳动间隔
const MIN_DELAY = 50; // 最小跳动间隔
@Component
export default class extends Vue {
private timer!: number;
private curIdx: number = 0; // 当前激活的索引
private count: number = 0; // 跳动次数
/**
* 跳动间隔
*/
private get delay() {
if (this.count > 10) {
return MIN_DELAY;
}
return MAX_DELAY - ((MAX_DELAY - MIN_DELAY) / 10) * this.count;
}
/**
* 执行抽奖动画
*/
private animate() {
// options 表示奖品列表,在上一篇文章有提及
if (this.curIdx >= this.options.length - 1) {
this.curIdx = 0;
} else {
this.curIdx++;
}
this.count--;
if (this.count <= 0) {
this.ended();
return;
}
this.timer = setTimeout(() => {
this.animate();
}, this.delay);
}
}
- 定义跳动次数为
count,表示激活状态的跳动次数,当count === 0时,则动画结束; - 通过递归调用
animate方法实现连贯的跳动效果,同时动画结束的时候触发ended方法; - 通过
MAX_DELAY跟MIN_DELAY实现最后几个格子跳动的放缓效果,增强抽奖体验;
事件
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
const ROLL = 3; // 跳动循环圈数
@Component
export default class extends Vue {
private timer!: number;
private prizeIdx: number = 0; // 中奖索引
private isPlaying: boolean = false;
/**
* 执行抽奖动画
*/
private animate() {
if (this.curIdx >= this.options.length - 1) {
this.curIdx = 0;
} else {
this.curIdx++;
}
this.count--;
if (this.count <= 0) {
this.ended();
return;
}
this.timer = setTimeout(() => {
this.animate();
}, this.delay);
}
/**
* 开始抽奖
*/
private raffle() {
if (this.isPlaying) {
uni.showToast({
title: "正在抽奖...",
icon: "none",
});
return;
}
this.isPlaying = true;
this.prizeIdx = this.beforeRaffle.call(this.$parent);
this.setCount();
this.animate();
}
/**
* 抽奖结束
*/
private ended() {
this.isPlaying = false;
this.$emit("on-ended", this.curIdx);
}
/**
* 设置跳动次数
*/
private setCount() {
const count = this.prizeIdx - this.curIdx;
const len = this.options.length;
this.count = ROLL * len + (count > 0 ? count : count + len);
}
destroyed() {
clearTimeout(this.timer);
}
}
点击按钮会触发raffle方法,出于安全考虑,一般中什么奖品的逻辑是在服务器端实现,前端只是实现播放动画以及展示中奖内容等,这里定义了开始抽奖前的钩子beforeRaffle,通过这个钩子返回的中奖索引prizeIdx,进而计算跳动次数count,实现动画,最后就是重置播放状态isPlaying。
至此,就完成了九宫格抽奖组件的实现。 最后,我们康康效果。