简易的九宫格抽奖
// 渐变动画 animation.js
export default class animation {
public myReq: number;
public fpsAni: number;
public maxSpeed: number;
public defaultSpeed: number;
public cycleNumber: number;
public DataArr: Record<string, any>[];
public running: Function | null;
public runend: Function | null;
constructor(DataArr: Record<string, any>[], RotateDir: number[], cycleNumber: number, minSpeed: number) {
this.DataArr = JSON.parse(JSON.stringify(DataArr));
// 最大速度
this.maxSpeed = 2;
this.cycleNumber = cycleNumber || 2;
this.myReq = 0;
this.fpsAni = 0;
// 最小速度
this.defaultSpeed = minSpeed || 15;
this.DataArr.forEach((element, index) => {
element.next = this.DataArr[RotateDir[index]];
});
this.running = null
this.runend = null
}
run(id: number, running: Function, runend: Function) {
const times: number[] = [];
let fps: number; // 帧率
const _this = this;
let counter = 0; // 计数器,当前遍历到哪个节点
let current = 0; // 当前数字值
let n = 0;
let currentObj = this.DataArr[0];
let tem = this.DataArr[0];
let drawId = id; // 中奖id
if (drawId > this.DataArr.length) {
drawId = Math.ceil(Math.random() * 8);
}
while (true) {
if (n > this.DataArr.length) {
console.error(`${drawId}不存在`);
return;
}
if (tem.id == drawId) {
break;
}
tem = tem.next;
n++;
}
const allCount =
id <= 8
? this.cycleNumber * this.DataArr.length + n
: this.cycleNumber * this.DataArr.length * 2;
// 加速区间
const addSpeed = this.defaultSpeed - this.maxSpeed;
// 减速区间
const reduceSpeed = allCount - (this.defaultSpeed - this.maxSpeed);
this.running = running;
this.runend = runend;
const refreshLoop = () => {
this.fpsAni = requestAnimationFrame(() => {
const now = performance.now();
while (times.length > 0 && times[0] <= now - 1000) {
times.shift();
}
times.push(now);
fps = times.length;
refreshLoop();
});
};
refreshLoop();
const step = () => {
if (counter < addSpeed) {
// 加速环节
if (current < Math.pow(_this.defaultSpeed - counter, 2)) {
if (fps < 40 || fps > 65) {
current = current + (_this.defaultSpeed - counter) * 3;
} else {
current += _this.defaultSpeed / 2;
}
} else {
current = 0;
// 往前移动一个;
counter++;
currentObj = currentObj.next;
currentObj.status = 1;
_this.running && _this.running(currentObj);
}
} else if (counter >= addSpeed && counter < reduceSpeed) {
// 匀速
if (current < _this.maxSpeed) {
current++;
} else {
// 计数清零
current = 0;
// 往前移动一个;
counter++;
currentObj = currentObj.next;
currentObj.status = 2;
_this.running && _this.running(currentObj);
}
} else if (counter >= reduceSpeed && counter < allCount) {
//减速环节
if (Math.sqrt(current) <= _this.defaultSpeed - (allCount - counter)) {
current = current + 10;
} else {
// 计数清零
current = 0;
// 往前移动一个;
counter++;
currentObj = currentObj.next;
currentObj.status = 3;
_this.running && _this.running(currentObj);
}
}
// 停止
if (counter >= allCount) {
_this.runend && _this.runend(currentObj);
cancelAnimationFrame(_this.myReq);
return;
}
_this.myReq = requestAnimationFrame(step);
}
this.myReq = requestAnimationFrame(step);
}
stop() {
cancelAnimationFrame(this.myReq);
cancelAnimationFrame(this.fpsAni);
}
}
// 使用
// dom
<div v-for="(item, i) in sourceData" :key="i" class="grid-item" :class="{ 'select': item.isSelect }" @click="runAni(i)">{{ i === 4 ? 'click' : item.name }}</div>
// 样式
<style lang="less" scoped>
.grid{
display: grid;
grid-template-columns: repeat(3, 50px);
grid-template-rows: repeat(3, 50px);
background: #fff;
border-radius: 16px;
font-size: 12px;
.grid-item{
background-color: blueviolet;
}
.grid-item.select{
background-color: chocolate;
}
}
</style>
// 样式end
import animation from '@/utils/animation'
const sourceData = ref<Record<string, any>[]>([
{ id: 1, name: '1 name', isSelect: false},
{ id: 2, name: '2 name', isSelect: false},
{ id: 3, name: '3 name', isSelect: false},
{ id: 4, name: '4 name', isSelect: false},
{ id: 5, name: '5 name', isSelect: false},
{ id: 6, name: '6 name', isSelect: false},
{ id: 7, name: '7 name', isSelect: false},
{ id: 8, name: '8 name', isSelect: false},
{ id: 9, name: '9 name', isSelect: false},
])
// 抽奖动画转动顺序
const rotateDir = [1, 2, 3, 4, 5, 6, 7, 8, 0]
const luckdrawfn = new animation(sourceData.value, rotateDir, 3, 20);
// 执行抽奖动画
const runAni = (i: number) => {
if (i !== 4) return
const id = 2
luckdrawfn.run(id, (params: Record<string, any>) => {
sourceData.value = sourceData.value.map((item: Record<string, any>) => {
return { ...item, isSelect: item.id === params.id}
})
}, (params: Record<string, any>) => {
console.log('end', params, params.id)
})
}
欢迎指正