v0.11: 解码晶体卡片 hover 增强 + StatsPanel selector 性能优化
DecodeArray CrystalCard: - hover 流光边框动画 (echo-border-flow) - hover 扫描线动画 (echo-scan) - T标签/tierLabel 上移至顶部 z-10, 不被底部覆盖层遮挡 - hover 底部显示晶体数值 (+6/+22/+90) + 解码提示 - 晶体图形 hover 旋转 200deg + scale 110% StatsPanel (page.tsx): - 全量订阅 useGameStore() → shallow 聚合 selector - 避免每 tick(250ms) 无关字段变化触发重渲染 globals.css: - 新增 echo-scan / echo-border-flow keyframes QA: VLM hover 效果 9/10, lint 零错误
This commit is contained in:
@@ -177,3 +177,17 @@
|
||||
0% { opacity: 0; transform: scale(0.92) translateY(8px); }
|
||||
100% { opacity: 1; transform: scale(1) translateY(0); }
|
||||
}
|
||||
|
||||
/* v0.11 晶体卡片 hover 扫描线 */
|
||||
@keyframes echo-scan {
|
||||
0% { top: 0%; opacity: 0; }
|
||||
20% { opacity: 1; }
|
||||
80% { opacity: 1; }
|
||||
100% { top: 100%; opacity: 0; }
|
||||
}
|
||||
|
||||
/* v0.11 晶体卡片 hover 边框流光 */
|
||||
@keyframes echo-border-flow {
|
||||
0% { background-position: 0% 50%; }
|
||||
100% { background-position: 200% 50%; }
|
||||
}
|
||||
|
||||
+27
-1
@@ -33,6 +33,7 @@ import { useGameLoop } from "@/hooks/useGameLoop";
|
||||
import { useAudioSync } from "@/hooks/useAudio";
|
||||
import { useGlobalTide } from "@/hooks/useGlobalTide";
|
||||
import { useGameStore } from "@/store/gameStore";
|
||||
import { shallow } from "zustand/shallow";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
@@ -567,7 +568,32 @@ function CrystalYieldPanel() {
|
||||
}
|
||||
|
||||
function StatsPanel() {
|
||||
const s = useGameStore();
|
||||
// v0.11 性能优化:用 shallow 聚合 selector 替代全量订阅 useGameStore(),
|
||||
// 避免每 tick(250ms)因无关字段变化触发重渲染。仅当所列字段引用变化才重渲染。
|
||||
const s = useGameStore(
|
||||
(st) => ({
|
||||
totalDecoded: st.totalDecoded,
|
||||
ascensions: st.ascensions,
|
||||
blueprints: st.blueprints,
|
||||
tech: st.tech,
|
||||
fragments: st.fragments,
|
||||
achievements: st.achievements,
|
||||
constellation: st.constellation,
|
||||
contact: st.contact,
|
||||
crystalsPerSec: st.crystalsPerSec,
|
||||
crystalCap: st.crystalCap,
|
||||
insightMult: st.insightMult,
|
||||
totalExpeditions: st.totalExpeditions,
|
||||
bossKills: st.bossKills,
|
||||
starTidesEncountered: st.starTidesEncountered,
|
||||
chronicle: st.chronicle,
|
||||
energy: st.energy,
|
||||
energyMax: st.energyMax,
|
||||
attributes: st.attributes,
|
||||
pendingAttrPoints: st.pendingAttrPoints,
|
||||
}),
|
||||
shallow
|
||||
);
|
||||
const achCount = Object.values(s.achievements).filter(Boolean).length;
|
||||
// 深空信标本地排行榜最高分(独立 localStorage)
|
||||
const [beaconBest, setBeaconBest] = useState<number | null>(null);
|
||||
|
||||
@@ -408,13 +408,33 @@ function CrystalCard({
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className="group relative aspect-square rounded-2xl border border-white/10 bg-black/40 hover:border-white/30 transition-all duration-300 hover:scale-[1.03] focus:outline-none overflow-hidden"
|
||||
className="group relative aspect-square rounded-2xl border border-white/10 bg-black/40 hover:border-white/40 transition-all duration-300 hover:scale-[1.04] focus:outline-none overflow-hidden"
|
||||
style={{ boxShadow: `inset 0 0 24px ${tierColor}22` }}
|
||||
>
|
||||
{/* v0.11 hover 流光边框 */}
|
||||
<div
|
||||
className="absolute inset-0 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"
|
||||
style={{
|
||||
background: `linear-gradient(120deg, transparent 30%, ${tierColor}40 50%, transparent 70%)`,
|
||||
backgroundSize: "200% 100%",
|
||||
animation: "echo-border-flow 2s linear infinite",
|
||||
mixBlendMode: "screen",
|
||||
}}
|
||||
/>
|
||||
{/* v0.11 hover 扫描线 */}
|
||||
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none overflow-hidden">
|
||||
<div
|
||||
className="absolute inset-x-0 h-px"
|
||||
style={{
|
||||
background: `linear-gradient(90deg, transparent, ${tierColor}, transparent)`,
|
||||
animation: "echo-scan 1.8s ease-in-out infinite",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/* 晶体图形 */}
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div
|
||||
className="relative h-12 w-12 transition-transform duration-500 group-hover:rotate-180"
|
||||
className="relative h-12 w-12 transition-transform duration-500 group-hover:rotate-[200deg] group-hover:scale-110"
|
||||
style={{ filter: `drop-shadow(0 0 12px ${tierColor}88)` }}
|
||||
>
|
||||
<div
|
||||
@@ -430,15 +450,22 @@ function CrystalCard({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* 标签 */}
|
||||
<div className="absolute top-1.5 left-1.5 text-[10px] font-mono px-1.5 py-0.5 rounded bg-black/50" style={{ color: tierColor }}>
|
||||
{/* v0.11 标签上移至顶部,z-10 避免被底部覆盖层遮挡 */}
|
||||
<div
|
||||
className="absolute top-1.5 left-1.5 z-10 text-[10px] font-mono font-semibold px-1.5 py-0.5 rounded bg-black/60 backdrop-blur-sm border"
|
||||
style={{ color: tierColor, borderColor: `${tierColor}40` }}
|
||||
>
|
||||
T{crystal.tier}
|
||||
</div>
|
||||
<div className="absolute bottom-1.5 right-1.5 text-[10px] text-white/70">
|
||||
<div className="absolute top-1.5 right-1.5 z-10 text-[9px] text-white/60 font-mono tracking-wider">
|
||||
{tierLabel}
|
||||
</div>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity flex items-end justify-center pb-2">
|
||||
<span className="text-[11px] font-medium text-white">解码 →</span>
|
||||
{/* v0.11 hover 底部显示晶体数值 + 解码提示 */}
|
||||
<div className="absolute inset-x-0 bottom-0 z-10 bg-gradient-to-t from-black/85 via-black/50 to-transparent pt-4 pb-1.5 px-1 text-center opacity-0 group-hover:opacity-100 transition-opacity duration-200">
|
||||
<div className="text-[12px] font-mono font-bold leading-none" style={{ color: tierColor }}>
|
||||
+{crystal.value}
|
||||
</div>
|
||||
<div className="text-[8px] text-white/60 uppercase tracking-[0.15em] mt-0.5">解码 →</div>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user