[auto] stamp-style judgment text at random positions with rotation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
main
name 2026-04-11 18:05:28 -07:00
parent 13d623dd20
commit 4a7054f320
2 changed files with 24 additions and 14 deletions

View File

@ -33,25 +33,24 @@
/* Judgment flash */
.ag-judgment {
position: absolute;
top: 38%;
left: 50%;
transform: translate(-50%, -50%);
font-family: system-ui, sans-serif;
font-size: 64px;
font-size: 72px;
font-weight: 900;
z-index: 3;
animation: judgment-pop 0.4s ease-out forwards;
text-shadow: 0 0 20px currentColor;
animation: judgment-stamp 0.6s ease-out forwards;
text-shadow: 0 0 20px currentColor, 0 0 40px currentColor;
letter-spacing: 0.05em;
}
.ag-judgment-perfect { color: #00ff6a; }
.ag-judgment-good { color: #ffe600; }
.ag-judgment-miss { color: #ff2222; }
@keyframes judgment-pop {
0% { opacity: 1; transform: translate(-50%, -50%) scale(0.5); }
30% { opacity: 1; transform: translate(-50%, -50%) scale(1.2); }
100% { opacity: 0; transform: translate(-50%, -50%) scale(1) translateY(-20px); }
@keyframes judgment-stamp {
0% { opacity: 1; scale: 2.5; }
15% { opacity: 1; scale: 1; }
60% { opacity: 0.9; scale: 1.05; }
100% { opacity: 0; scale: 1; }
}
/* Target zone */

View File

@ -48,14 +48,17 @@ export const ArrowGame = forwardRef<ArrowGameHandle, Props>(({ active = true, on
const nextId = useRef(0)
const [arrows, setArrows] = useState<GameArrow[]>([])
const [score, setScore] = useState({ perfect: 0, good: 0, miss: 0, combo: 0 })
const [judgment, setJudgment] = useState<{ text: string; key: number } | null>(null)
const [judgment, setJudgment] = useState<{ text: string; key: number; x: number; y: number; rot: number } | null>(null)
const judgmentTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
const [targetPulse, setTargetPulse] = useState<{ dir: ArrowDirection; key: number } | null>(null)
const showJudgment = useCallback((text: string) => {
if (judgmentTimer.current) clearTimeout(judgmentTimer.current)
setJudgment({ text, key: Date.now() })
judgmentTimer.current = setTimeout(() => setJudgment(null), 400)
const x = 25 + Math.random() * 50 // 25%-75% horizontal
const y = 25 + Math.random() * 40 // 25%-65% vertical
const rot = (Math.random() - 0.5) * 20 // -10 to +10 degrees
setJudgment({ text, key: Date.now(), x, y, rot })
judgmentTimer.current = setTimeout(() => setJudgment(null), 600)
}, [])
useImperativeHandle(ref, () => ({
@ -178,7 +181,15 @@ export const ArrowGame = forwardRef<ArrowGameHandle, Props>(({ active = true, on
</div>
{judgment && (
<div key={judgment.key} className={`ag-judgment ag-judgment-${judgment.text.toLowerCase()}`}>
<div
key={judgment.key}
className={`ag-judgment ag-judgment-${judgment.text.toLowerCase()}`}
style={{
left: `${judgment.x}%`,
top: `${judgment.y}%`,
transform: `translate(-50%, -50%) rotate(${judgment.rot}deg)`,
}}
>
{judgment.text}
</div>
)}