[auto] stamp-style judgment text at random positions with rotation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>main
parent
13d623dd20
commit
4a7054f320
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in New Issue