sard-uniapp
Version:
sard-uniapp 是一套基于 Uniapp + Vue3 框架开发的兼容多端的 UI 组件库
160 lines (159 loc) • 5.09 kB
JavaScript
import { computed, onScopeDispose, ref, unref } from 'vue';
import { random, ticker } from '../utils';
const defaultOptions = {
minSpeed: 0.1,
maxSpeed: 0.4,
accelTime: 2500,
decelTime: 2500,
easeIn: (k) => k * k,
easeOut: (k) => k * (2 - k),
startDelay: 0,
endDelay: 0,
};
export const useLuckyDraw = (options) => {
const { count, minSpeed, maxSpeed, accelTime, decelTime, complete, easeIn, easeOut, startDelay, endDelay, } = Object.assign({}, defaultOptions, options);
let stage = 'INITIAL';
let currentIndex = 0;
const activeIndex = ref();
const playing = ref(false);
let startTime = 0;
let accelFrames = 0;
let decelStopIndex;
let decelStopActiveIndex;
let decelStartIndex = 0;
const getDecelStopIndex = () => {
const interval = unref(accelTime) / accelFrames;
let i = 0;
let mockCurrentIndex = currentIndex;
let mockActiveIndex = 0;
while (++i) {
let progress = (interval * i) / unref(decelTime);
if (progress >= 1) {
progress = 1;
}
const currentSpeed = unref(maxSpeed) -
easeOut(progress) * (unref(maxSpeed) - unref(minSpeed));
mockCurrentIndex += currentSpeed;
mockActiveIndex = mockCurrentIndex % unref(count) >> 0;
if (progress === 1 && mockActiveIndex === decelStopActiveIndex) {
break;
}
}
return mockCurrentIndex >> 0;
};
const tick = () => {
switch (stage) {
case 'START_DELAY': {
if (Date.now() - startTime >= unref(startDelay)) {
stage = 'ACCELERATED';
startTime = Date.now();
}
break;
}
case 'ACCELERATED': {
accelFrames++;
let progress = (Date.now() - startTime) / unref(accelTime);
if (progress >= 1) {
progress = 1;
}
const currentSpeed = unref(minSpeed) +
easeIn(progress) * (unref(maxSpeed) - unref(minSpeed));
currentIndex += currentSpeed;
if (progress === 1) {
stage = 'CONSTANT_SPEED';
}
break;
}
case 'CONSTANT_SPEED': {
currentIndex += unref(maxSpeed);
if (decelStopActiveIndex !== undefined) {
stage = 'DECELERATED';
startTime = Date.now();
}
break;
}
case 'DECELERATED': {
if (decelStopIndex === undefined) {
decelStopIndex = getDecelStopIndex();
decelStartIndex = currentIndex;
}
let progress = (Date.now() - startTime) / unref(decelTime);
if (progress >= 1) {
progress = 1;
}
currentIndex =
decelStartIndex +
easeOut(progress) * (decelStopIndex - decelStartIndex);
if (progress === 1) {
if (unref(endDelay) === 0) {
stage = 'DONE';
}
else {
stage = 'END_DELAY';
startTime = Date.now();
}
}
break;
}
case 'END_DELAY': {
if (Date.now() - startTime >= unref(endDelay)) {
stage = 'DONE';
}
break;
}
case 'DONE': {
const index = decelStopActiveIndex;
pause();
complete?.(index);
break;
}
}
activeIndex.value = currentIndex;
};
const play = () => {
if (playing.value) {
return;
}
playing.value = true;
startTime = Date.now();
if (unref(startDelay) === 0) {
stage = 'ACCELERATED';
}
else {
stage = 'START_DELAY';
}
ticker.add(tick);
};
const pause = () => {
ticker.remove(tick);
playing.value = false;
startTime = 0;
accelFrames = 0;
decelStopIndex = undefined;
decelStopActiveIndex = undefined;
decelStartIndex = 0;
};
const reset = () => {
pause();
stage = 'INITIAL';
currentIndex = 0;
activeIndex.value = undefined;
};
const stop = (index) => {
if (decelStopActiveIndex === undefined) {
decelStopActiveIndex =
index === undefined ? random(0, unref(count) - 1) : index;
}
};
onScopeDispose(() => {
pause();
});
return {
play,
stop,
pause,
reset,
playing: computed(() => playing.value),
activeIndex: computed(() => activeIndex.value),
};
};