简简单单写个九宫格抽奖组件(二)

389 阅读2分钟

前言

周末去参加好哥们的婚礼了,今天才赶回来,所以更新晚了。现在继续把九宫格抽奖组件后面的两个模块:动画以及事件写完。

动画

说到 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);
	}
}
  1. 定义跳动次数为count,表示激活状态的跳动次数,当count === 0时,则动画结束;
  2. 通过递归调用animate方法实现连贯的跳动效果,同时动画结束的时候触发ended方法;
  3. 通过MAX_DELAYMIN_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

至此,就完成了九宫格抽奖组件的实现。 最后,我们康康效果。

源码

github.com/SuperIron/v…

参考文献