feat: 优化整体项目ui

This commit is contained in:
JiaJun
2026-05-22 17:58:52 +08:00
parent 44c984d59e
commit 046f250ce3
56 changed files with 2149 additions and 700 deletions

View File

@@ -31,6 +31,29 @@ type GameRoundSlice = Pick<
| 'trends'
>
export type RevealAnimationPhase = 'idle' | 'spinning' | 'stopping' | 'result'
export type RewardAnimationType = 'none' | 'small' | 'big'
export interface RevealAnimationState {
pendingRewardType: RewardAnimationType
phase: RevealAnimationPhase
revealKey: string | null
rewardType: RewardAnimationType
roundId: string | null
winningCellId: number | null
}
function createIdleRevealAnimation(): RevealAnimationState {
return {
pendingRewardType: 'none',
phase: 'idle',
revealKey: null,
rewardType: 'none',
roundId: null,
winningCellId: null,
}
}
function resolveRecentActiveChipId(
chips: Chip[],
selections: BetSelection[],
@@ -54,14 +77,25 @@ function resolveRecentActiveChipId(
export interface GameRoundStoreState extends GameRoundSlice {
activeChipId: string
clearSelections: () => void
clearRewardAnimation: () => void
finishRevealAnimation: () => void
hydrateRound: (snapshot: GameRoundSlice) => void
placeBet: (cellId: number) => void
playPreparedRevealAnimation: (roundId?: string | null) => void
prepareRevealAnimation: (input: {
hasSmallReward: boolean
revealKey: string
roundId: string
winningCellId: number
}) => void
recentSuccessfulSelections: BetSelection[]
revealAnimation: RevealAnimationState
removeSelectionsForCell: (cellId: number) => void
restoreRecentSuccessfulSelections: () => boolean
setRecentSuccessfulSelections: (selections: BetSelection[]) => void
selectChip: (chipId: string) => void
setPhase: (phase: RoundPhase) => void
showJackpotReward: (roundId?: string | null) => void
syncRound: (round: Partial<RoundSnapshot>) => void
upsertSelections: (selections: BetSelection[]) => void
}
@@ -69,6 +103,7 @@ export interface GameRoundStoreState extends GameRoundSlice {
function createInitialRoundState(): GameRoundSlice & {
activeChipId: string
recentSuccessfulSelections: BetSelection[]
revealAnimation: RevealAnimationState
} {
const snapshot = createEmptyGameBootstrapSnapshot()
@@ -79,6 +114,7 @@ function createInitialRoundState(): GameRoundSlice & {
history: snapshot.history,
maxSelectionCount: snapshot.maxSelectionCount,
recentSuccessfulSelections: [],
revealAnimation: createIdleRevealAnimation(),
round: snapshot.round,
selections: snapshot.selections,
trends: snapshot.trends,
@@ -90,6 +126,37 @@ export const useGameRoundStore = create<GameRoundStoreState>()((set) => ({
clearSelections: () => {
set({ selections: [] })
},
clearRewardAnimation: () => {
set((state) => ({
revealAnimation: {
...state.revealAnimation,
rewardType: 'none',
},
}))
},
finishRevealAnimation: () => {
set((state) => {
if (
state.revealAnimation.phase !== 'stopping' &&
state.revealAnimation.phase !== 'spinning'
) {
return state
}
const rewardType =
state.revealAnimation.rewardType === 'big'
? 'big'
: state.revealAnimation.pendingRewardType
return {
revealAnimation: {
...state.revealAnimation,
phase: 'result',
rewardType,
},
}
})
},
hydrateRound: (snapshot) => {
set((state) => ({
activeChipId: getChipById(snapshot.chips, state.activeChipId)
@@ -101,6 +168,10 @@ export const useGameRoundStore = create<GameRoundStoreState>()((set) => ({
chips: snapshot.chips,
history: snapshot.history,
maxSelectionCount: snapshot.maxSelectionCount,
revealAnimation:
snapshot.round.phase === 'betting' || snapshot.round.phase === 'waiting'
? createIdleRevealAnimation()
: state.revealAnimation,
round: snapshot.round,
selections: snapshot.selections,
trends: snapshot.trends,
@@ -143,6 +214,61 @@ export const useGameRoundStore = create<GameRoundStoreState>()((set) => ({
}
})
},
playPreparedRevealAnimation: (roundId) => {
set((state) => {
const nextRoundId = roundId ?? state.revealAnimation.roundId
if (
state.revealAnimation.winningCellId === null ||
state.revealAnimation.revealKey === null ||
state.revealAnimation.phase !== 'idle'
) {
return state
}
return {
revealAnimation: {
...state.revealAnimation,
phase: 'stopping',
roundId: nextRoundId,
},
}
})
},
prepareRevealAnimation: ({
hasSmallReward,
revealKey,
roundId,
winningCellId,
}) => {
set((state) => {
if (state.revealAnimation.revealKey === revealKey) {
return state
}
const pendingRewardType =
state.revealAnimation.pendingRewardType === 'big'
? 'big'
: hasSmallReward
? 'small'
: 'none'
return {
revealAnimation: {
...state.revealAnimation,
pendingRewardType,
phase: 'idle',
revealKey,
rewardType:
state.revealAnimation.rewardType === 'big'
? 'big'
: state.revealAnimation.rewardType,
roundId,
winningCellId,
},
}
})
},
recentSuccessfulSelections: [],
removeSelectionsForCell: (cellId) => {
set((state) => ({
@@ -215,13 +341,38 @@ export const useGameRoundStore = create<GameRoundStoreState>()((set) => ({
nextState.selections = []
}
if (phase === 'locked' || phase === 'revealing') {
nextState.selections = []
}
if (phase === 'betting' || phase === 'waiting') {
nextState.revealAnimation = createIdleRevealAnimation()
}
return nextState
})
},
showJackpotReward: (roundId) => {
set((state) => {
const nextRoundId =
roundId ?? state.round.id ?? state.revealAnimation.roundId
const isResultReady = state.revealAnimation.phase === 'result'
return {
revealAnimation: {
...state.revealAnimation,
pendingRewardType: 'big',
rewardType: isResultReady ? 'big' : state.revealAnimation.rewardType,
roundId: nextRoundId,
},
}
})
},
syncRound: (round) => {
set((state) => {
const previousRound = state.round
const nextRound = {
...state.round,
...previousRound,
...round,
}
@@ -233,6 +384,17 @@ export const useGameRoundStore = create<GameRoundStoreState>()((set) => ({
nextState.selections = []
}
if (nextRound.phase === 'locked' || nextRound.phase === 'revealing') {
nextState.selections = []
}
if (
nextRound.id !== previousRound.id &&
(nextRound.phase === 'betting' || nextRound.phase === 'waiting')
) {
nextState.revealAnimation = createIdleRevealAnimation()
}
return nextState
})
},

View File

@@ -1,43 +1,13 @@
import { create } from 'zustand'
import { INITIAL_MODAL_VISIBILITY, MODAL_KEYS } from '@/constants'
import type { WithdrawTopupType } from '@/type'
export const MODAL_KEYS = [
/**@description 桌面端登录弹窗*/
'desktopLogin',
/**@description 桌面端注册弹窗*/
'desktopRegister',
/**@description 桌面端多语言弹窗*/
'desktopLanguage',
/**@description 桌面端规则弹窗*/
'desktopRules',
/**@description 桌面端用户信息弹窗*/
'desktopUserInfo',
/**@description 桌面端公告弹窗*/
'desktopNotice',
/**@description 桌面端自动托管弹窗*/
'desktopAutoSetting',
/**@description 桌面端充值提现前置选择弹窗*/
'desktopProcedures',
/**@description 桌面端充值/提现弹窗*/
'desktopWithdrawTopup',
] as const
export { MODAL_KEYS }
export type ModalKey = (typeof MODAL_KEYS)[number]
type ModalVisibilityMap = Record<ModalKey, boolean>
const INITIAL_MODAL_VISIBILITY: ModalVisibilityMap = {
desktopLogin: false,
desktopRegister: false,
desktopLanguage: false,
desktopRules: false,
desktopUserInfo: false,
desktopNotice: false,
desktopAutoSetting: false,
desktopProcedures: false,
desktopWithdrawTopup: false,
}
export interface ModalStoreState {
modals: ModalVisibilityMap
withdrawTopupType: WithdrawTopupType