This commit is contained in:
59
src/components/game-canvas/NpcAffinityEffectBadge.tsx
Normal file
59
src/components/game-canvas/NpcAffinityEffectBadge.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Heart } from 'lucide-react';
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
import type { StoryNpcAffinityEffect } from '../../types';
|
||||
|
||||
interface NpcAffinityEffectBadgeProps {
|
||||
effect: StoryNpcAffinityEffect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天结算后的好感度浮出特效。
|
||||
* 仅负责表现层,不承担任何数值计算。
|
||||
*/
|
||||
export function NpcAffinityEffectBadge({
|
||||
effect,
|
||||
}: NpcAffinityEffectBadgeProps) {
|
||||
const isPositive = effect.delta > 0;
|
||||
const deltaText = `${effect.delta > 0 ? '+' : ''}${effect.delta}`;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={effect.eventId}
|
||||
initial={{ opacity: 0, y: 24, scale: 0.8 }}
|
||||
animate={{ opacity: [0, 1, 1, 0], y: [24, -8, -26, -44], scale: [0.8, 1.08, 1, 0.92] }}
|
||||
transition={{ duration: 1.45, ease: 'easeOut' }}
|
||||
className="pointer-events-none absolute -top-14 left-1/2 z-[12] flex -translate-x-1/2 items-center gap-1 rounded-full border px-2.5 py-1 shadow-[0_10px_24px_rgba(0,0,0,0.35)] backdrop-blur-[2px]"
|
||||
data-testid={`npc-affinity-effect-${effect.npcId}`}
|
||||
aria-label={`好感度变化 ${deltaText}`}
|
||||
>
|
||||
{isPositive ? (
|
||||
<>
|
||||
<div className="absolute inset-0 rounded-full bg-[radial-gradient(circle_at_top,rgba(255,255,255,0.3),transparent_60%)]" />
|
||||
<div className="absolute -inset-1 rounded-full bg-rose-400/18 blur-md" />
|
||||
<div className="relative flex items-center gap-1 text-rose-50">
|
||||
<Heart className="h-3.5 w-3.5 fill-current" />
|
||||
<span className="text-xs font-semibold tracking-[0.08em]">
|
||||
{deltaText}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="absolute inset-0 rounded-full bg-[radial-gradient(circle_at_top,rgba(255,255,255,0.18),transparent_60%)]" />
|
||||
<div className="absolute -inset-1 rounded-full bg-slate-400/15 blur-md" />
|
||||
<div className="relative text-xs font-semibold tracking-[0.08em] text-slate-100">
|
||||
{deltaText}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div
|
||||
className={`absolute inset-0 rounded-full border ${
|
||||
isPositive
|
||||
? 'border-rose-200/45 bg-rose-500/18'
|
||||
: 'border-slate-200/35 bg-slate-700/30'
|
||||
}`}
|
||||
/>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user