feat(home): 重构抽奖逻辑并优化初始化流程 Feature #91

This commit is contained in:
LOG1997
2025-12-08 22:56:01 +08:00
parent 9f4769a4c9
commit 9c06540068
4 changed files with 204 additions and 14 deletions

View File

@@ -14,7 +14,7 @@ import useStore from '@/store'
import { selectCard } from '@/utils'
import { rgba } from '@/utils/color'
import { LotteryStatus } from './type'
import { confettiFire, createSphereVertices, createTableVertices, initTableData } from './util'
import { confettiFire, createSphereVertices, createTableVertices, getRandomElements, initTableData } from './utils'
export function useViewModel() {
const toast = useToast()
@@ -52,7 +52,7 @@ export function useViewModel() {
const personPool = ref<IPersonConfig[]>([])
const intervalTimer = ref<any>(null)
function init() {
function initThreeJs() {
const felidView = 40
const width = window.innerWidth
const height = window.innerHeight
@@ -358,14 +358,22 @@ export function useViewModel() {
}
}
luckyCount.value = leftover < luckyCount.value ? leftover : luckyCount.value
for (let i = 0; i < luckyCount.value; i++) {
if (personPool.value.length > 0) {
// 解决随机元素概率过于不均等问题
const randomIndex = Math.floor(Math.random() * (personPool.value.length - 1))
luckyTargets.value.push(personPool.value[randomIndex])
personPool.value.splice(randomIndex, 1)
// 重构抽奖函数
luckyTargets.value = getRandomElements(personPool.value, luckyCount.value)
luckyTargets.value.forEach((item) => {
const index = personPool.value.findIndex(person => person.id === item.id)
if (index > -1) {
personPool.value.splice(index, 1)
}
}
})
// for (let i = 0; i < luckyCount.value; i++) {
// if (personPool.value.length > 0) {
// // 解决随机元素概率过于不均等问题
// const randomIndex = Math.floor(Math.random() * (personPool.value.length - 1))
// luckyTargets.value.push(personPool.value[randomIndex])
// personPool.value.splice(randomIndex, 1)
// }
// }
toast.open({
// message: `现在抽取${currentPrize.value.name} ${leftover}人`,
@@ -575,13 +583,32 @@ export function useViewModel() {
// 刷新页面
window.location.reload()
}
const init = () => {
const startTime = Date.now()
const maxWaitTime = 2000 // 2秒
const checkAndInit = () => {
// 如果人员列表有数据或者等待时间超过2秒则执行初始化
if (allPersonList.value.length > 0 || (Date.now() - startTime) >= maxWaitTime) {
console.log('初始化完成')
tableData.value = initTableData({ allPersonList: allPersonList.value, rowCount: rowCount.value })
initThreeJs()
animation()
containerRef.value!.style.color = `${textColor}`
randomBallData()
window.addEventListener('keydown', listenKeyboard)
}
else {
console.log('等待人员列表数据...')
// 继续等待
setTimeout(checkAndInit, 100) // 每100毫秒检查一次
}
}
checkAndInit()
}
onMounted(() => {
tableData.value = initTableData({ allPersonList: allPersonList.value, rowCount: rowCount.value })
init()
animation()
containerRef.value!.style.color = `${textColor}`
randomBallData()
window.addEventListener('keydown', listenKeyboard)
})
onUnmounted(() => {
nextTick(() => {
@@ -591,6 +618,11 @@ export function useViewModel() {
intervalTimer.value = null
window.removeEventListener('keydown', listenKeyboard)
})
// watch(() => allPersonList.value, (newVal) => {
// if (newVal.length) {
// init()
// }
// })
return {
setDefaultPersonList,

View File

@@ -0,0 +1,2 @@
export * from './random'
export * from './table'

View File

@@ -0,0 +1,131 @@
import type { IPersonConfig } from '@/types/storeType'
import confetti from 'canvas-confetti'
import { Object3D, Vector3 } from 'three'
import { filterData } from '@/utils'
/**
* @description 初始化表格数据
* @param0 allPersonList 所有人的列表
* @param1 rowCount 行数默认是7行
* @returns 表格数据
*/
export function initTableData({ allPersonList, rowCount }: { allPersonList: IPersonConfig[], rowCount: number }): IPersonConfig[] {
let tableData: IPersonConfig[] = []
if (allPersonList.length <= 0) {
return []
}
const totalCount = rowCount * 7
const allPersonLength = allPersonList.length
if (allPersonLength < totalCount) {
tableData = Array.from({ length: totalCount }, () => JSON.parse(JSON.stringify(allPersonList))).flat()
}
else {
tableData = allPersonList.slice(0, totalCount)
}
tableData = filterData(tableData.slice(0, totalCount), rowCount)
return tableData
}
/**
* @description 横铺图形:处理数据,把每个卡片在界面的位置写入
* @param0 tableData 表格数据
* @param1 rowCount 每行有多少个元素
* @param2 cardSize 卡片的大小
* @returns Object3D[]
*/
export function createTableVertices({ tableData, rowCount, cardSize }: { tableData: IPersonConfig[], rowCount: number, cardSize: { width: number, height: number } }): Object3D[] {
const tableLen = tableData.length
const objects: Object3D[] = []
for (let i = 0; i < tableLen; i++) {
const object = new Object3D()
object.position.x = tableData[i].x * (cardSize.width + 40) - rowCount * 90
object.position.y = -tableData[i].y * (cardSize.height + 20) + 1000
object.position.z = 0
objects.push(object)
// targets.table.push(object)
}
return objects
}
/**
* @description 创建球体
* @param0 objectsLength 物体的个数
* @returns Object3D[]
*/
export function createSphereVertices({ objectsLength }: { objectsLength: number }): Object3D[] {
let i = 0
const resObjects: Object3D[] = []
// const objLength = objects.value.length
const vector = new Vector3()
for (; i < objectsLength; ++i) {
const phi = Math.acos(-1 + (2 * i) / objectsLength)
const theta = Math.sqrt(objectsLength * Math.PI) * phi
const object = new Object3D()
object.position.x = 800 * Math.cos(theta) * Math.sin(phi)
object.position.y = 800 * Math.sin(theta) * Math.sin(phi)
object.position.z = -800 * Math.cos(phi)
// rotation object
vector.copy(object.position).multiplyScalar(2)
object.lookAt(vector)
resObjects.push(object)
}
return resObjects
}
export function confettiFire() {
const duration = 3 * 1000
const end = Date.now() + duration;
(function frame() {
// launch a few confetti from the left edge
confetti({
particleCount: 2,
angle: 60,
spread: 55,
origin: { x: 0 },
})
// and launch a few from the right edge
confetti({
particleCount: 2,
angle: 120,
spread: 55,
origin: { x: 1 },
})
// keep going until we are out of time
if (Date.now() < end) {
requestAnimationFrame(frame)
}
}())
centerFire(0.25, {
spread: 26,
startVelocity: 55,
})
centerFire(0.2, {
spread: 60,
})
centerFire(0.35, {
spread: 100,
decay: 0.91,
scalar: 0.8,
})
centerFire(0.1, {
spread: 120,
startVelocity: 25,
decay: 0.92,
scalar: 1.2,
})
centerFire(0.1, {
spread: 120,
startVelocity: 45,
})
}
function centerFire(particleRatio: number, opts: any) {
const count = 200
confetti({
origin: { y: 0.7 },
...opts,
particleCount: Math.floor(count * particleRatio),
})
}