fix(game): 修复移动端动物组件渲染和奖励数据显示问题
- 修复了GitNexus索引统计信息中的符号、关系和执行流数量 - 为桌面端动物组件的投注开始警告添加了z-index类以确保正确显示层级 - 修复了游戏回合存储中奖励金额和类型的返回逻辑,避免显示旧奖励数据 - 调整了移动端金币揭示效果的内边距样式以改善视觉效果 - 重构了移动端动物网格组件,添加了容器引用并修复了揭示动画定位问题 - 清理了未使用的移动揭示帧偏移常量并优化了网格布局结构 - 更新了移动端控制组件中的内边距和间距以改善UI布局 - 在移动端头部组件中添加了新的图像资源和CSS工具函数 - 调整了移动端头部组件中的按钮大小、间距和对齐方式 - 优化了移动端工具栏按钮的样式类以实现更好的响应式设计
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<!-- gitnexus:start -->
|
||||
# GitNexus — Code Intelligence
|
||||
|
||||
This project is indexed by GitNexus as **36-character-flower** (2879 symbols, 5735 relationships, 245 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
|
||||
This project is indexed by GitNexus as **36-character-flower** (3072 symbols, 6072 relationships, 262 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
|
||||
|
||||
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- gitnexus:start -->
|
||||
# GitNexus — Code Intelligence
|
||||
|
||||
This project is indexed by GitNexus as **36-character-flower** (2879 symbols, 5735 relationships, 245 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
|
||||
This project is indexed by GitNexus as **36-character-flower** (3072 symbols, 6072 relationships, 262 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
|
||||
|
||||
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
|
||||
|
||||
|
||||
@@ -470,7 +470,7 @@ export function DesktopAnimal({
|
||||
) : null}
|
||||
|
||||
<DesktopAnimalOverlay showStopOverlay={showStopOverlay} />
|
||||
<RoundBettingStartAlert />
|
||||
<RoundBettingStartAlert className="z-50" />
|
||||
|
||||
{showStandbyState ? (
|
||||
<button
|
||||
|
||||
@@ -18,7 +18,6 @@ const SETTLEMENT_REVEAL_SETTLE_DURATION_MS = 800
|
||||
const SETTLEMENT_REVEAL_RESULT_HOLD_MS = 1_000
|
||||
const SETTLEMENT_REVEAL_MIN_STEP_MS = 180
|
||||
const SETTLEMENT_REVEAL_MAX_STEP_MS = 960
|
||||
const MOBILE_REVEAL_FRAME_OFFSET_PX = 2
|
||||
|
||||
function getRandomAnimalId(ids: number[], currentId: number | null) {
|
||||
if (ids.length === 0) {
|
||||
@@ -66,6 +65,7 @@ export function MobileAnimal({
|
||||
const prefersReducedMotion = useReducedMotion()
|
||||
const animalIds = useMemo(() => FLOWER_IMAGE_LIST.map((item) => item.id), [])
|
||||
const containerRef = useRef<HTMLElement | null>(null)
|
||||
const animalGridRef = useRef<HTMLDivElement | null>(null)
|
||||
const cellRefs = useRef(new Map<number, HTMLButtonElement>())
|
||||
const [revealCellId, setRevealCellId] = useState<number | null>(null)
|
||||
const [revealFrame, setRevealFrame] = useState<{
|
||||
@@ -214,7 +214,7 @@ export function MobileAnimal({
|
||||
}
|
||||
|
||||
const syncRevealFrame = () => {
|
||||
const container = containerRef.current
|
||||
const container = animalGridRef.current
|
||||
const cell = cellRefs.current.get(revealCellId)
|
||||
|
||||
if (!container || !cell) {
|
||||
@@ -251,230 +251,241 @@ export function MobileAnimal({
|
||||
>
|
||||
<MobileStatusLine />
|
||||
|
||||
<div className="relative z-10 grid w-full grid-cols-4 gap-design-3">
|
||||
{FLOWER_IMAGE_LIST.map((item) => {
|
||||
const selectionMeta = selectionByCell[item.id]
|
||||
const hasPlacedSelection = Boolean(selectionMeta)
|
||||
const isMarqueeActive = showStandbyState && item.id === marqueeId
|
||||
const isRevealWinner =
|
||||
!shouldHideRevealHighlight &&
|
||||
(isRevealResult || isRevealHoldingResult) &&
|
||||
revealWinningCellId === item.id
|
||||
const warningType =
|
||||
cellWarning?.cellId === item.id ? cellWarning.type : null
|
||||
const showCellWarning = warningType !== null
|
||||
const warningLabel =
|
||||
warningType === 'balance'
|
||||
? t('gameDesktop.animal.insufficientBalanceRecharge')
|
||||
: warningType === 'betLimit'
|
||||
? t('gameDesktop.animal.betLimitExceeded')
|
||||
: t('gameDesktop.animal.selectionLimitReached')
|
||||
<div ref={animalGridRef} className="relative z-10 w-full">
|
||||
<div className="grid w-full grid-cols-4 gap-design-3">
|
||||
{FLOWER_IMAGE_LIST.map((item) => {
|
||||
const selectionMeta = selectionByCell[item.id]
|
||||
const hasPlacedSelection = Boolean(selectionMeta)
|
||||
const isMarqueeActive = showStandbyState && item.id === marqueeId
|
||||
const isRevealWinner =
|
||||
!shouldHideRevealHighlight &&
|
||||
(isRevealResult || isRevealHoldingResult) &&
|
||||
revealWinningCellId === item.id
|
||||
const warningType =
|
||||
cellWarning?.cellId === item.id ? cellWarning.type : null
|
||||
const showCellWarning = warningType !== null
|
||||
const warningLabel =
|
||||
warningType === 'balance'
|
||||
? t('gameDesktop.animal.insufficientBalanceRecharge')
|
||||
: warningType === 'betLimit'
|
||||
? t('gameDesktop.animal.betLimitExceeded')
|
||||
: t('gameDesktop.animal.selectionLimitReached')
|
||||
|
||||
return (
|
||||
<motion.button
|
||||
key={item.id}
|
||||
ref={(node) => {
|
||||
if (node) {
|
||||
cellRefs.current.set(item.id, node)
|
||||
} else {
|
||||
cellRefs.current.delete(item.id)
|
||||
}
|
||||
}}
|
||||
type="button"
|
||||
disabled={lockInteraction || showStopOverlay}
|
||||
onClick={() => handleSelect(item.id)}
|
||||
animate={
|
||||
showCellWarning
|
||||
? {
|
||||
rotate: [0, -1.8, 1.4, -1, 0.6, 0],
|
||||
scale: [1, 1.015, 0.992, 1.01, 1],
|
||||
x: [0, -2, 2, -1, 1, 0],
|
||||
}
|
||||
: {
|
||||
rotate: 0,
|
||||
scale: 1,
|
||||
x: 0,
|
||||
}
|
||||
}
|
||||
transition={
|
||||
showCellWarning
|
||||
? {
|
||||
duration: 0.46,
|
||||
ease: 'easeInOut',
|
||||
}
|
||||
: { duration: 0.16, ease: 'easeOut' }
|
||||
}
|
||||
className={cn(
|
||||
'relative flex h-design-36 flex-col items-center justify-center overflow-hidden rounded-[calc(var(--design-unit)*6)] border border-transparent transition-[transform,border-color,box-shadow,opacity] duration-150',
|
||||
lockInteraction
|
||||
? 'cursor-not-allowed opacity-90'
|
||||
: 'cursor-pointer hover:-translate-y-[1px]',
|
||||
isMarqueeActive &&
|
||||
'border-[rgba(121,255,250,1)] shadow-[0_0_calc(var(--design-unit)*18)_rgba(85,255,247,0.98),0_0_calc(var(--design-unit)*34)_rgba(39,245,255,0.88),inset_0_0_calc(var(--design-unit)*26)_rgba(112,255,248,0.34)]',
|
||||
isRevealRunning &&
|
||||
'border-[rgba(104,255,249,0.76)] shadow-[0_0_calc(var(--design-unit)*10)_rgba(68,244,255,0.46),0_0_calc(var(--design-unit)*22)_rgba(37,214,255,0.28),inset_0_0_calc(var(--design-unit)*16)_rgba(115,255,247,0.18)] brightness-115 saturate-125',
|
||||
isRevealWinner &&
|
||||
'border-[rgba(121,255,250,0.72)] shadow-[0_0_calc(var(--design-unit)*12)_rgba(81,248,255,0.54),0_0_calc(var(--design-unit)*22)_rgba(30,199,255,0.32),inset_0_0_calc(var(--design-unit)*18)_rgba(125,255,249,0.24)] brightness-110 saturate-120',
|
||||
showCellWarning &&
|
||||
'border-[rgba(255,92,92,1)] shadow-[0_0_calc(var(--design-unit)*18)_rgba(255,88,88,0.56),0_0_calc(var(--design-unit)*28)_rgba(255,44,44,0.32),inset_0_0_calc(var(--design-unit)*18)_rgba(255,126,126,0.3)]',
|
||||
!showStandbyState && !hasPlacedSelection && 'opacity-95',
|
||||
itemClassName,
|
||||
)}
|
||||
>
|
||||
<SmartImage
|
||||
src={animalBorderImage}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
priority
|
||||
showSkeleton={false}
|
||||
className="pointer-events-none absolute inset-0 z-20 h-full w-full"
|
||||
imgClassName="object-fill"
|
||||
/>
|
||||
<span className="pointer-events-none absolute left-design-6 top-design-4 z-30 text-design-10 font-bold leading-none text-[#4BFFFE]">
|
||||
{String(item.id).padStart(2, '0')}
|
||||
</span>
|
||||
<motion.span
|
||||
aria-hidden="true"
|
||||
return (
|
||||
<motion.button
|
||||
key={item.id}
|
||||
ref={(node) => {
|
||||
if (node) {
|
||||
cellRefs.current.set(item.id, node)
|
||||
} else {
|
||||
cellRefs.current.delete(item.id)
|
||||
}
|
||||
}}
|
||||
type="button"
|
||||
disabled={lockInteraction || showStopOverlay}
|
||||
onClick={() => handleSelect(item.id)}
|
||||
animate={
|
||||
showCellWarning
|
||||
? {
|
||||
opacity: [0.45, 1, 0.6, 1, 0.82],
|
||||
rotate: [0, -1.8, 1.4, -1, 0.6, 0],
|
||||
scale: [1, 1.015, 0.992, 1.01, 1],
|
||||
x: [0, -2, 2, -1, 1, 0],
|
||||
}
|
||||
: {
|
||||
rotate: 0,
|
||||
scale: 1,
|
||||
x: 0,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
transition={
|
||||
showCellWarning
|
||||
? {
|
||||
duration: 0.52,
|
||||
duration: 0.46,
|
||||
ease: 'easeInOut',
|
||||
}
|
||||
: undefined
|
||||
: { duration: 0.16, ease: 'easeOut' }
|
||||
}
|
||||
className={cn(
|
||||
'pointer-events-none absolute inset-[calc(var(--design-unit)*1)] rounded-[calc(var(--design-unit)*6)] opacity-0 transition-opacity duration-150',
|
||||
'relative flex h-design-36 flex-col items-center justify-center overflow-hidden rounded-[calc(var(--design-unit)*6)] border border-transparent transition-[transform,border-color,box-shadow,opacity] duration-150',
|
||||
lockInteraction
|
||||
? 'cursor-not-allowed opacity-90'
|
||||
: 'cursor-pointer hover:-translate-y-[1px]',
|
||||
isMarqueeActive &&
|
||||
'bg-[radial-gradient(circle_at_center,rgba(129,255,250,0.48)_0%,rgba(94,255,247,0.18)_38%,rgba(43,236,255,0.08)_56%,transparent_76%)] opacity-100 shadow-[0_0_calc(var(--design-unit)*12)_rgba(119,255,249,0.98),0_0_calc(var(--design-unit)*28)_rgba(53,246,255,0.9),0_0_calc(var(--design-unit)*44)_rgba(37,241,255,0.58),inset_0_0_calc(var(--design-unit)*20)_rgba(163,255,250,0.52)]',
|
||||
'border-[rgba(121,255,250,1)] shadow-[0_0_calc(var(--design-unit)*18)_rgba(85,255,247,0.98),0_0_calc(var(--design-unit)*34)_rgba(39,245,255,0.88),inset_0_0_calc(var(--design-unit)*26)_rgba(112,255,248,0.34)]',
|
||||
isRevealRunning &&
|
||||
'bg-[radial-gradient(circle_at_center,rgba(128,255,250,0.36)_0%,rgba(77,244,255,0.16)_40%,rgba(27,183,255,0.07)_68%,transparent_88%)] opacity-100 shadow-[0_0_calc(var(--design-unit)*12)_rgba(95,249,255,0.46),inset_0_0_calc(var(--design-unit)*18)_rgba(151,255,250,0.24)]',
|
||||
'border-[rgba(104,255,249,0.76)] shadow-[0_0_calc(var(--design-unit)*10)_rgba(68,244,255,0.46),0_0_calc(var(--design-unit)*22)_rgba(37,214,255,0.28),inset_0_0_calc(var(--design-unit)*16)_rgba(115,255,247,0.18)] brightness-115 saturate-125',
|
||||
isRevealWinner &&
|
||||
'bg-[radial-gradient(circle_at_center,rgba(128,255,250,0.34)_0%,rgba(67,226,255,0.16)_38%,rgba(25,131,255,0.07)_58%,transparent_76%)] opacity-100 shadow-[0_0_calc(var(--design-unit)*12)_rgba(92,248,255,0.42),inset_0_0_calc(var(--design-unit)*18)_rgba(126,255,250,0.24)]',
|
||||
'border-[rgba(121,255,250,0.72)] shadow-[0_0_calc(var(--design-unit)*12)_rgba(81,248,255,0.54),0_0_calc(var(--design-unit)*22)_rgba(30,199,255,0.32),inset_0_0_calc(var(--design-unit)*18)_rgba(125,255,249,0.24)] brightness-110 saturate-120',
|
||||
showCellWarning &&
|
||||
'bg-[radial-gradient(circle_at_center,rgba(255,106,106,0.34)_0%,rgba(255,58,58,0.18)_42%,rgba(108,0,0,0.2)_78%,transparent_100%)] opacity-100',
|
||||
'border-[rgba(255,92,92,1)] shadow-[0_0_calc(var(--design-unit)*18)_rgba(255,88,88,0.56),0_0_calc(var(--design-unit)*28)_rgba(255,44,44,0.32),inset_0_0_calc(var(--design-unit)*18)_rgba(255,126,126,0.3)]',
|
||||
!showStandbyState && !hasPlacedSelection && 'opacity-95',
|
||||
itemClassName,
|
||||
)}
|
||||
/>
|
||||
{!showStandbyState && !hasPlacedSelection ? (
|
||||
<span
|
||||
>
|
||||
<SmartImage
|
||||
src={animalBorderImage}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute inset-[calc(var(--design-unit)*1)] z-20 rounded-[calc(var(--design-unit)*6)] bg-[rgba(4,16,24,0.52)] shadow-[inset_0_0_calc(var(--design-unit)*12)_rgba(3,9,14,0.56)]"
|
||||
priority
|
||||
showSkeleton={false}
|
||||
className="pointer-events-none absolute inset-0 z-20 h-full w-full"
|
||||
imgClassName="object-fill"
|
||||
/>
|
||||
) : null}
|
||||
<SmartImage
|
||||
src={item.animalUrl}
|
||||
alt={`animal-${item.id}`}
|
||||
className={cn(
|
||||
'absolute left-[1.5%] right-[1.5%] top-[2.9%] bottom-[2.9%] z-10 overflow-hidden rounded-[calc(var(--design-unit)*6)]',
|
||||
isRevealRunning &&
|
||||
'brightness-115 saturate-125 drop-shadow-[0_0_calc(var(--design-unit)*8)_rgba(101,250,255,0.42)]',
|
||||
isRevealWinner &&
|
||||
'brightness-110 saturate-120 drop-shadow-[0_0_calc(var(--design-unit)*9)_rgba(106,250,255,0.44)]',
|
||||
imageClassName,
|
||||
)}
|
||||
imgClassName="object-fill"
|
||||
/>
|
||||
{showCellWarning ? (
|
||||
<span className="pointer-events-none absolute left-design-6 top-design-4 z-30 text-design-10 font-bold leading-none text-[#4BFFFE]">
|
||||
{String(item.id).padStart(2, '0')}
|
||||
</span>
|
||||
<motion.span
|
||||
initial={{ opacity: 0, scale: 0.94 }}
|
||||
animate={{
|
||||
opacity: [0.2, 1, 0.92],
|
||||
scale: [0.96, 1.02, 1],
|
||||
boxShadow: [
|
||||
'inset 0 0 calc(var(--design-unit)*10) rgba(255,108,108,0.16), 0 0 calc(var(--design-unit)*8) rgba(255,60,60,0.12)',
|
||||
'inset 0 0 calc(var(--design-unit)*20) rgba(255,108,108,0.28), 0 0 calc(var(--design-unit)*20) rgba(255,60,60,0.28)',
|
||||
'inset 0 0 calc(var(--design-unit)*16) rgba(255,108,108,0.22), 0 0 calc(var(--design-unit)*18) rgba(255,60,60,0.2)',
|
||||
],
|
||||
}}
|
||||
transition={{ duration: 0.5, ease: 'easeOut' }}
|
||||
className="pointer-events-none absolute inset-[calc(var(--design-unit)*2)] z-30 flex flex-col items-center justify-center gap-design-2 rounded-[calc(var(--design-unit)*6)] border border-[rgba(255,126,126,0.9)] bg-[rgba(61,0,0,0.58)] px-design-2 py-design-2 text-center"
|
||||
>
|
||||
aria-hidden="true"
|
||||
animate={
|
||||
showCellWarning
|
||||
? {
|
||||
opacity: [0.45, 1, 0.6, 1, 0.82],
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
transition={
|
||||
showCellWarning
|
||||
? {
|
||||
duration: 0.52,
|
||||
ease: 'easeInOut',
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
className={cn(
|
||||
'pointer-events-none absolute inset-[calc(var(--design-unit)*1)] rounded-[calc(var(--design-unit)*6)] opacity-0 transition-opacity duration-150',
|
||||
isMarqueeActive &&
|
||||
'bg-[radial-gradient(circle_at_center,rgba(129,255,250,0.48)_0%,rgba(94,255,247,0.18)_38%,rgba(43,236,255,0.08)_56%,transparent_76%)] opacity-100 shadow-[0_0_calc(var(--design-unit)*12)_rgba(119,255,249,0.98),0_0_calc(var(--design-unit)*28)_rgba(53,246,255,0.9),0_0_calc(var(--design-unit)*44)_rgba(37,241,255,0.58),inset_0_0_calc(var(--design-unit)*20)_rgba(163,255,250,0.52)]',
|
||||
isRevealRunning &&
|
||||
'bg-[radial-gradient(circle_at_center,rgba(128,255,250,0.36)_0%,rgba(77,244,255,0.16)_40%,rgba(27,183,255,0.07)_68%,transparent_88%)] opacity-100 shadow-[0_0_calc(var(--design-unit)*12)_rgba(95,249,255,0.46),inset_0_0_calc(var(--design-unit)*18)_rgba(151,255,250,0.24)]',
|
||||
isRevealWinner &&
|
||||
'bg-[radial-gradient(circle_at_center,rgba(128,255,250,0.34)_0%,rgba(67,226,255,0.16)_38%,rgba(25,131,255,0.07)_58%,transparent_76%)] opacity-100 shadow-[0_0_calc(var(--design-unit)*12)_rgba(92,248,255,0.42),inset_0_0_calc(var(--design-unit)*18)_rgba(126,255,250,0.24)]',
|
||||
showCellWarning &&
|
||||
'bg-[radial-gradient(circle_at_center,rgba(255,106,106,0.34)_0%,rgba(255,58,58,0.18)_42%,rgba(108,0,0,0.2)_78%,transparent_100%)] opacity-100',
|
||||
)}
|
||||
/>
|
||||
{!showStandbyState && !hasPlacedSelection ? (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute inset-[calc(var(--design-unit)*1)] z-20 rounded-[calc(var(--design-unit)*6)] bg-[rgba(4,16,24,0.52)] shadow-[inset_0_0_calc(var(--design-unit)*12)_rgba(3,9,14,0.56)]"
|
||||
/>
|
||||
) : null}
|
||||
<SmartImage
|
||||
src={item.animalUrl}
|
||||
alt={`animal-${item.id}`}
|
||||
className={cn(
|
||||
'absolute left-[1.5%] right-[1.5%] top-[2.9%] bottom-[2.9%] z-10 overflow-hidden rounded-[calc(var(--design-unit)*6)]',
|
||||
isRevealRunning &&
|
||||
'brightness-115 saturate-125 drop-shadow-[0_0_calc(var(--design-unit)*8)_rgba(101,250,255,0.42)]',
|
||||
isRevealWinner &&
|
||||
'brightness-110 saturate-120 drop-shadow-[0_0_calc(var(--design-unit)*9)_rgba(106,250,255,0.44)]',
|
||||
imageClassName,
|
||||
)}
|
||||
imgClassName="object-fill"
|
||||
/>
|
||||
{showCellWarning ? (
|
||||
<motion.span
|
||||
initial={{ opacity: 0, scale: 0.94 }}
|
||||
animate={{
|
||||
opacity: [0.72, 1, 0.92],
|
||||
scale: [0.94, 1.08, 1],
|
||||
opacity: [0.2, 1, 0.92],
|
||||
scale: [0.96, 1.02, 1],
|
||||
boxShadow: [
|
||||
'inset 0 0 calc(var(--design-unit)*10) rgba(255,108,108,0.16), 0 0 calc(var(--design-unit)*8) rgba(255,60,60,0.12)',
|
||||
'inset 0 0 calc(var(--design-unit)*20) rgba(255,108,108,0.28), 0 0 calc(var(--design-unit)*20) rgba(255,60,60,0.28)',
|
||||
'inset 0 0 calc(var(--design-unit)*16) rgba(255,108,108,0.22), 0 0 calc(var(--design-unit)*18) rgba(255,60,60,0.2)',
|
||||
],
|
||||
}}
|
||||
transition={{ duration: 0.38, ease: 'easeOut' }}
|
||||
className="flex h-design-10 w-design-10 items-center justify-center"
|
||||
transition={{ duration: 0.5, ease: 'easeOut' }}
|
||||
className="pointer-events-none absolute inset-[calc(var(--design-unit)*2)] z-30 flex flex-col items-center justify-center gap-design-2 rounded-[calc(var(--design-unit)*6)] border border-[rgba(255,126,126,0.9)] bg-[rgba(61,0,0,0.58)] px-design-2 py-design-2 text-center"
|
||||
>
|
||||
<TriangleAlert className="h-design-10 w-design-10 text-[#FFD0D0] drop-shadow-[0_0_calc(var(--design-unit)*5)_rgba(255,92,92,0.45)]" />
|
||||
<motion.span
|
||||
animate={{
|
||||
opacity: [0.72, 1, 0.92],
|
||||
scale: [0.94, 1.08, 1],
|
||||
}}
|
||||
transition={{ duration: 0.38, ease: 'easeOut' }}
|
||||
className="flex h-design-10 w-design-10 items-center justify-center"
|
||||
>
|
||||
<TriangleAlert className="h-design-10 w-design-10 text-[#FFD0D0] drop-shadow-[0_0_calc(var(--design-unit)*5)_rgba(255,92,92,0.45)]" />
|
||||
</motion.span>
|
||||
<motion.span
|
||||
animate={{
|
||||
opacity: [0.7, 1, 0.96],
|
||||
scale: [0.98, 1.03, 1],
|
||||
}}
|
||||
transition={{ duration: 0.42, ease: 'easeOut' }}
|
||||
className="text-design-6 font-bold leading-tight tracking-[0.02em] text-[#FFE0E0] [text-shadow:0_0_calc(var(--design-unit)*6)_rgba(255,132,132,0.42)]"
|
||||
>
|
||||
{warningLabel}
|
||||
</motion.span>
|
||||
</motion.span>
|
||||
<motion.span
|
||||
animate={{
|
||||
opacity: [0.7, 1, 0.96],
|
||||
scale: [0.98, 1.03, 1],
|
||||
}}
|
||||
transition={{ duration: 0.42, ease: 'easeOut' }}
|
||||
className="text-design-6 font-bold leading-tight tracking-[0.02em] text-[#FFE0E0] [text-shadow:0_0_calc(var(--design-unit)*6)_rgba(255,132,132,0.42)]"
|
||||
>
|
||||
{warningLabel}
|
||||
</motion.span>
|
||||
</motion.span>
|
||||
) : null}
|
||||
{hasPlacedSelection ? (
|
||||
<span className="pointer-events-none absolute inset-0 z-20 flex items-center justify-center">
|
||||
<span className="flex min-w-design-36 items-center justify-center gap-design-1 rounded-full border border-[rgba(162,242,255,0.48)] bg-[linear-gradient(180deg,rgba(7,23,34,0.88),rgba(5,14,22,0.96))] px-design-3 py-design-1 shadow-[0_0_calc(var(--design-unit)*8)_rgba(70,245,255,0.18)]">
|
||||
<SmartImage
|
||||
src={diamondIcon}
|
||||
alt="diamond"
|
||||
className="h-design-9 w-design-9 shrink-0 object-contain"
|
||||
/>
|
||||
<span className="text-design-8 font-semibold leading-none tracking-[0.03em] text-[#D8FBFF]">
|
||||
{selectionMeta.amount}
|
||||
) : null}
|
||||
{hasPlacedSelection ? (
|
||||
<span className="pointer-events-none absolute inset-0 z-20 flex items-center justify-center">
|
||||
<span className="flex min-w-design-36 items-center justify-center gap-design-1 rounded-full border border-[rgba(162,242,255,0.48)] bg-[linear-gradient(180deg,rgba(7,23,34,0.88),rgba(5,14,22,0.96))] px-design-3 py-design-1 shadow-[0_0_calc(var(--design-unit)*8)_rgba(70,245,255,0.18)]">
|
||||
<SmartImage
|
||||
src={diamondIcon}
|
||||
alt="diamond"
|
||||
className="h-design-9 w-design-9 shrink-0 object-contain"
|
||||
/>
|
||||
<span className="text-design-8 font-semibold leading-none tracking-[0.03em] text-[#D8FBFF]">
|
||||
{selectionMeta.amount}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
) : null}
|
||||
</motion.button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{revealFrame ? (
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
'pointer-events-none absolute left-0 top-0 z-40 transform-gpu transition-[height,transform,width]',
|
||||
prefersReducedMotion
|
||||
? 'duration-0'
|
||||
: isRevealSettlingResult
|
||||
? 'duration-[800ms] ease-[cubic-bezier(0.16,1,0.3,1)]'
|
||||
: 'duration-[160ms] ease-[cubic-bezier(0.22,1,0.36,1)]',
|
||||
)}
|
||||
style={{
|
||||
height: revealFrame.height + MOBILE_REVEAL_FRAME_OFFSET_PX * 2,
|
||||
transform: `translate(${revealFrame.left - MOBILE_REVEAL_FRAME_OFFSET_PX}px, ${revealFrame.top - MOBILE_REVEAL_FRAME_OFFSET_PX}px)`,
|
||||
width: revealFrame.width + MOBILE_REVEAL_FRAME_OFFSET_PX * 2,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="mobile-gold-reveal-glow rounded-[calc(var(--design-unit)*7)]"
|
||||
style={
|
||||
prefersReducedMotion
|
||||
? {
|
||||
animation: 'none',
|
||||
opacity: 0.36,
|
||||
transform: 'scale(1)',
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
<div className="mobile-gold-reveal-static-border rounded-[calc(var(--design-unit)*7)]" />
|
||||
<div
|
||||
className="mobile-gold-reveal-shell rounded-[calc(var(--design-unit)*7)]"
|
||||
style={prefersReducedMotion ? { animation: 'none' } : undefined}
|
||||
/>
|
||||
) : null}
|
||||
</motion.button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<MobileAnimalOverlay showStopOverlay={showStopOverlay} />
|
||||
{revealFrame ? (
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
'pointer-events-none absolute left-0 top-0 z-30 transform-gpu transition-[height,transform,width]',
|
||||
prefersReducedMotion
|
||||
? 'duration-0'
|
||||
: isRevealSettlingResult
|
||||
? 'duration-[800ms] ease-[cubic-bezier(0.16,1,0.3,1)]'
|
||||
: 'duration-[160ms] ease-[cubic-bezier(0.22,1,0.36,1)]',
|
||||
)}
|
||||
style={{
|
||||
height: revealFrame.height,
|
||||
transform: `translate(${revealFrame.left}px, ${revealFrame.top}px)`,
|
||||
width: revealFrame.width,
|
||||
}}
|
||||
>
|
||||
<SmartImage
|
||||
src={animalBorderImage}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
priority
|
||||
showSkeleton={false}
|
||||
className="pointer-events-none absolute inset-0 z-30 h-full w-full"
|
||||
imgClassName="object-fill"
|
||||
/>
|
||||
<div
|
||||
className="mobile-gold-reveal-glow z-10 rounded-[calc(var(--design-unit)*7)]"
|
||||
style={
|
||||
prefersReducedMotion
|
||||
? {
|
||||
animation: 'none',
|
||||
opacity: 0.36,
|
||||
transform: 'scale(1)',
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
<div className="mobile-gold-reveal-static-border z-20 rounded-[calc(var(--design-unit)*7)]" />
|
||||
<div
|
||||
className="mobile-gold-reveal-shell z-20 rounded-[calc(var(--design-unit)*7)]"
|
||||
style={prefersReducedMotion ? { animation: 'none' } : undefined}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<MobileAnimalOverlay showStopOverlay={showStopOverlay} />
|
||||
</div>
|
||||
|
||||
{showStandbyState ? (
|
||||
<button
|
||||
|
||||
@@ -148,12 +148,12 @@ export function MobileControl() {
|
||||
<SmartBackground
|
||||
src={chipBg}
|
||||
size="100% 100%"
|
||||
className="relative z-10 flex h-full min-w-0 flex-1 items-center bg-center bg-no-repeat px-design-8"
|
||||
className="relative z-10 flex h-full min-w-0 flex-1 items-center bg-center bg-no-repeat pl-design-8 pr-design-1"
|
||||
>
|
||||
<SmartBackground
|
||||
src={chipLineBg}
|
||||
size="100% 100%"
|
||||
className="flex h-design-32 min-w-0 flex-1 items-center justify-between gap-design-2 overflow-visible px-design-4"
|
||||
className="flex h-design-32 min-w-0 items-center justify-between gap-design-7 overflow-visible"
|
||||
>
|
||||
{chips.map((chip) => {
|
||||
const isSelected = chip.id === selectedChipId
|
||||
@@ -290,7 +290,7 @@ export function MobileControl() {
|
||||
<SmartBackground
|
||||
src={mobileAddReduceBg}
|
||||
size="100% 100%"
|
||||
className="relative z-20 -ml-design-6 flex h-full w-design-103 shrink-0 items-center justify-center gap-design-6 bg-center bg-no-repeat px-design-9 text-design-13 font-bold"
|
||||
className="relative z-20 !ml-[calc(var(--design-unit)*-8)] flex h-full w-design-103 shrink-0 items-center justify-center gap-design-5 bg-center bg-no-repeat px-design-7 text-design-13 font-bold"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -8,12 +8,14 @@ import {
|
||||
} from 'lucide-react'
|
||||
import { motion } from 'motion/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import add from '@/assets/game/add.webp'
|
||||
import avatar from '@/assets/system/avatar.webp'
|
||||
import chatImage from '@/assets/system/chat.webp'
|
||||
import diamond from '@/assets/system/diamond.webp'
|
||||
import logo from '@/assets/system/logo.webp'
|
||||
import { SmartImage } from '@/components/smart-image.tsx'
|
||||
import { useHeaderClockLabel, useHeaderVm } from '@/hooks/use-header-vm'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useModalStore } from '@/store'
|
||||
|
||||
function MobileHeaderClock() {
|
||||
@@ -87,6 +89,15 @@ export function MobileHeader() {
|
||||
'common-neon-inset flex h-design-19 shrink-0 cursor-pointer items-center justify-center gap-design-5 !rounded-[3px] !px-design-5 !py-0 text-design-7 leading-none transition-opacity hover:opacity-85'
|
||||
const accountButtonClassName =
|
||||
'common-neon-inset flex h-design-19 items-center justify-end !rounded-[3px] !py-0 text-design-7 leading-none transition-[opacity,transform] duration-150 group-hover:opacity-90 group-active:scale-[0.98]'
|
||||
const toolbarButtonClassName = cn(
|
||||
actionButtonClassName,
|
||||
'min-w-0 shrink gap-design-3 !px-design-3',
|
||||
)
|
||||
const compactToolbarButtonClassName = cn(toolbarButtonClassName, 'flex-[0.8]')
|
||||
const wideToolbarButtonClassName = cn(
|
||||
toolbarButtonClassName,
|
||||
'flex-[1.2] !px-design-4',
|
||||
)
|
||||
|
||||
return (
|
||||
<header className="relative z-30 h-design-72">
|
||||
@@ -101,7 +112,7 @@ export function MobileHeader() {
|
||||
/>
|
||||
</div>
|
||||
{authStatus === 'authenticated' ? (
|
||||
<div className="flex h-full min-w-0 flex-1 items-center justify-end gap-design-7 px-design-9">
|
||||
<div className="flex h-full min-w-0 flex-1 items-center justify-end gap-design-20 px-design-9">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onOpenUserInfo}
|
||||
@@ -111,10 +122,10 @@ export function MobileHeader() {
|
||||
src={avatar}
|
||||
alt="avatar"
|
||||
priority
|
||||
className="absolute left-0 z-20 h-design-25 w-design-25 rounded-full"
|
||||
className="absolute left-0 z-20 h-design-25 w-design-25 -translate-x-1/2 rounded-full"
|
||||
/>
|
||||
<div
|
||||
className={`${accountButtonClassName} w-design-92 pl-design-28 pr-design-6`}
|
||||
className={`${accountButtonClassName} w-design-80 pl-design-28 pr-design-6`}
|
||||
>
|
||||
<span className="truncate">
|
||||
{currentUser?.username || '--'}
|
||||
@@ -131,22 +142,20 @@ export function MobileHeader() {
|
||||
src={diamond}
|
||||
alt="diamond"
|
||||
priority
|
||||
className="absolute left-0 z-20 h-design-25 w-design-25"
|
||||
className="absolute left-0 z-20 h-design-25 w-design-25 -translate-x-1/2"
|
||||
/>
|
||||
<div
|
||||
className={`${accountButtonClassName} w-design-90 gap-design-4 pl-design-26 pr-design-4`}
|
||||
className={`${accountButtonClassName} w-design-80 gap-design-4 !pr-design-4`}
|
||||
>
|
||||
<span className="min-w-0 truncate">
|
||||
{currentUser?.coin || '--'}
|
||||
</span>
|
||||
{/*<div className="common-neon-inset flex h-design-15 w-design-15 shrink-0 items-center justify-center !rounded-[3px] !p-0">*/}
|
||||
{/* <Plus*/}
|
||||
{/* aria-hidden="true"*/}
|
||||
{/* className="h-design-10 w-design-10 shrink-0"*/}
|
||||
{/* color="#57B8BF"*/}
|
||||
{/* strokeWidth={2.5}*/}
|
||||
{/* />*/}
|
||||
{/*</div>*/}
|
||||
<SmartImage
|
||||
src={add}
|
||||
alt="add"
|
||||
priority
|
||||
className="h-design-13 w-design-13 shrink-0 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
@@ -197,9 +206,9 @@ export function MobileHeader() {
|
||||
</motion.button>
|
||||
</div>
|
||||
|
||||
<div className={'w-full px-design-10 '}>
|
||||
<div className="flex h-design-29 w-full items-center gap-design-5 my-design-5 rounded-sm border-[#0A353E] border-1">
|
||||
<div className="flex h-design-19 w-design-43 shrink-0 items-center justify-center !rounded-[3px] !px-design-6 !py-0">
|
||||
<div className="w-full px-design-10">
|
||||
<div className="my-design-5 flex h-design-29 w-full min-w-0 items-center gap-design-5 rounded-sm border-1 border-[#0A353E] px-design-5">
|
||||
<div className="flex h-design-19 w-design-40 shrink-0 items-center justify-center !rounded-[3px]">
|
||||
<div className={signalPresentation.toneClassName}>
|
||||
<SignalBars
|
||||
activeBars={signalPresentation.activeBars}
|
||||
@@ -224,7 +233,7 @@ export function MobileHeader() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={onOpenRules}
|
||||
className={`${actionButtonClassName} !px-design-8`}
|
||||
className={compactToolbarButtonClassName}
|
||||
>
|
||||
<Info className="h-design-8 w-design-8 shrink-0" color="#57B8BF" />
|
||||
<div className="min-w-0 truncate">
|
||||
@@ -235,7 +244,7 @@ export function MobileHeader() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={onOpenNotice}
|
||||
className={`${actionButtonClassName} !px-design-8`}
|
||||
className={wideToolbarButtonClassName}
|
||||
>
|
||||
<Mail className="h-design-8 w-design-8 shrink-0" color="#57B8BF" />
|
||||
<div className="min-w-0 truncate">
|
||||
@@ -246,7 +255,7 @@ export function MobileHeader() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleSoundEnabled}
|
||||
className={`${actionButtonClassName} !px-design-8`}
|
||||
className={compactToolbarButtonClassName}
|
||||
>
|
||||
{isSoundEnabled ? (
|
||||
<Volume2
|
||||
@@ -266,7 +275,7 @@ export function MobileHeader() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={onOpenLanguage}
|
||||
className={`${actionButtonClassName} !px-design-8 justify-between`}
|
||||
className={wideToolbarButtonClassName}
|
||||
>
|
||||
<SmartImage
|
||||
src={currentLanguageOption.icon}
|
||||
|
||||
@@ -159,7 +159,8 @@ export function MobileStatusLine() {
|
||||
|
||||
export function MobileStatusMetrics() {
|
||||
const { t } = useTranslation()
|
||||
const { limitLabel, oddsLabel, streakLabel } = useGameStatusVm()
|
||||
const { limitLabel, oddsLabel, streakLabel, streakValue } = useGameStatusVm()
|
||||
const showStreakLimitOnly = typeof streakValue === 'number' && streakValue > 1
|
||||
|
||||
return (
|
||||
<SmartBackground
|
||||
@@ -168,24 +169,35 @@ export function MobileStatusMetrics() {
|
||||
className="relative flex h-design-26 w-full items-center overflow-visible bg-center bg-no-repeat px-design-10 font-bold leading-none text-[#CBD3D5]"
|
||||
style={{ fontSize: 'calc(var(--design-unit) * 7)' }}
|
||||
>
|
||||
<div
|
||||
className="pointer-events-none absolute inset-y-design-1 left-0 w-[68%] bg-center bg-no-repeat opacity-80"
|
||||
style={{
|
||||
backgroundImage: `url(${streakBg})`,
|
||||
backgroundSize: '118% 155%',
|
||||
}}
|
||||
/>
|
||||
<div className="relative z-10 flex min-w-0 flex-1 items-center justify-center">
|
||||
{t('gameDesktop.status.odds')}:
|
||||
<span className="ml-design-3 text-[#E3D171]">{oddsLabel}</span>
|
||||
</div>
|
||||
<div className="relative z-10 flex min-w-0 flex-1 items-center justify-center gap-design-2">
|
||||
<SmartImage className="h-design-11 w-design-9" alt="fire" src={fire} />
|
||||
<span>{t('gameDesktop.status.streak')}:</span>
|
||||
<span className="bg-gradient-to-b from-[#EBA661] to-[#FCC785] bg-clip-text text-transparent">
|
||||
{streakLabel}
|
||||
</span>
|
||||
</div>
|
||||
{showStreakLimitOnly ? (
|
||||
<div
|
||||
className="pointer-events-none absolute left-0 right-0 z-0 bg-center bg-no-repeat opacity-95"
|
||||
style={{
|
||||
backgroundImage: `url(${streakBg})`,
|
||||
backgroundSize: '120% 150%',
|
||||
bottom: 'calc(var(--design-unit)*-5)',
|
||||
top: 'calc(var(--design-unit)*-6)',
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<div className="relative z-10 flex min-w-0 flex-1 items-center justify-center">
|
||||
{t('gameDesktop.status.odds')}:
|
||||
<span className="ml-design-3 text-[#E3D171]">{oddsLabel}</span>
|
||||
</div>
|
||||
<div className="relative z-10 flex min-w-0 flex-1 items-center justify-center gap-design-2">
|
||||
<SmartImage
|
||||
className="h-design-11 w-design-9"
|
||||
alt="fire"
|
||||
src={fire}
|
||||
/>
|
||||
<span>{t('gameDesktop.status.streak')}:</span>
|
||||
<span className="bg-gradient-to-b from-[#EBA661] to-[#FCC785] bg-clip-text text-transparent">
|
||||
{streakLabel}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="relative z-10 flex min-w-0 flex-1 items-center justify-center gap-design-2">
|
||||
<SmartImage className="h-design-10 w-design-8" alt="lock" src={lock} />
|
||||
<span>{t('gameDesktop.status.limit')}:</span>
|
||||
|
||||
@@ -256,17 +256,19 @@ function applyBetWinMessage(message: GameSocketMessage) {
|
||||
return
|
||||
}
|
||||
|
||||
useGameRoundStore.getState().setPendingBetWinReward({
|
||||
isJackpot: betWinData.is_jackpot,
|
||||
revealKey: `${betWinData.period_no}:${betWinData.result_number ?? 'pending'}:${betWinData.total_win}`,
|
||||
roundId: betWinData.period_no,
|
||||
totalWin: betWinData.total_win,
|
||||
winningCellId: betWinData.result_number,
|
||||
})
|
||||
useGameAutoHostingStore.getState().recordBetWin({
|
||||
isJackpot: betWinData.is_jackpot,
|
||||
singleWinAmount: toOptionalNumber(betWinData.total_win) ?? null,
|
||||
})
|
||||
if (betWinData.is_win) {
|
||||
useGameRoundStore.getState().setPendingBetWinReward({
|
||||
isJackpot: betWinData.is_jackpot,
|
||||
revealKey: `${betWinData.period_no}:${betWinData.result_number ?? 'pending'}:${betWinData.total_win}`,
|
||||
roundId: betWinData.period_no,
|
||||
totalWin: betWinData.total_win,
|
||||
winningCellId: betWinData.result_number,
|
||||
})
|
||||
useGameAutoHostingStore.getState().recordBetWin({
|
||||
isJackpot: betWinData.is_jackpot,
|
||||
singleWinAmount: toOptionalNumber(betWinData.total_win) ?? null,
|
||||
})
|
||||
}
|
||||
|
||||
if (!currentUser) {
|
||||
return
|
||||
|
||||
@@ -208,10 +208,10 @@ export const useGameRoundStore = create<GameRoundStoreState>()((set) => ({
|
||||
: state.revealAnimation.revealKey,
|
||||
rewardAmount: hasPendingReward
|
||||
? state.revealAnimation.pendingRewardAmount
|
||||
: state.revealAnimation.rewardAmount,
|
||||
: null,
|
||||
rewardType: hasPendingReward
|
||||
? state.revealAnimation.pendingRewardType
|
||||
: state.revealAnimation.rewardType,
|
||||
: 'none',
|
||||
roundId: hasPendingReward
|
||||
? state.revealAnimation.pendingRewardRoundId
|
||||
: state.revealAnimation.roundId,
|
||||
|
||||
@@ -640,7 +640,7 @@
|
||||
.mobile-gold-reveal-shell {
|
||||
--gold-angle: 0deg;
|
||||
position: absolute;
|
||||
inset: calc(var(--design-unit) * 0.2);
|
||||
inset: calc(var(--design-unit) * 2.2);
|
||||
border-radius: calc(var(--design-unit) * 7);
|
||||
overflow: hidden;
|
||||
clip-path: inset(0 round calc(var(--design-unit) * 7));
|
||||
@@ -698,7 +698,7 @@
|
||||
|
||||
.mobile-gold-reveal-static-border {
|
||||
position: absolute;
|
||||
inset: calc(var(--design-unit) * 0.7);
|
||||
inset: calc(var(--design-unit) * 2.7);
|
||||
border-radius: calc(var(--design-unit) * 7);
|
||||
border: calc(var(--design-unit) * 2.8) solid rgba(181, 138, 40, 0.98);
|
||||
box-shadow:
|
||||
@@ -709,7 +709,7 @@
|
||||
|
||||
.mobile-gold-reveal-glow {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
inset: calc(var(--design-unit) * 1.4);
|
||||
border-radius: calc(var(--design-unit) * 8);
|
||||
background: radial-gradient(
|
||||
circle at center,
|
||||
|
||||
Reference in New Issue
Block a user