[auto] pulse target arrow on step detection for visual feedback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
main
name 2026-04-11 18:03:45 -07:00
parent dbc259c3b4
commit f22de64108
2 changed files with 17 additions and 1 deletions

View File

@ -77,6 +77,16 @@
padding: 4px;
}
.ag-target-pulse {
animation: target-beat 0.3s ease-out;
}
@keyframes target-beat {
0% { transform: translateX(-50%) scale(1); opacity: 0.5; filter: drop-shadow(0 0 0px white); }
30% { transform: translateX(-50%) scale(1.4); opacity: 1; filter: drop-shadow(0 0 20px white); }
100% { transform: translateX(-50%) scale(1); opacity: 0.35; filter: drop-shadow(0 0 0px white); }
}
/* Scrolling arrows */
.ag-arrow {
position: absolute;

View File

@ -50,6 +50,7 @@ export const ArrowGame = forwardRef<ArrowGameHandle, Props>(({ active = true, on
const [score, setScore] = useState({ perfect: 0, good: 0, miss: 0, combo: 0 })
const [judgment, setJudgment] = useState<{ text: string; key: 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)
@ -61,6 +62,7 @@ export const ArrowGame = forwardRef<ArrowGameHandle, Props>(({ active = true, on
handleStep(stepDir: StepDirection) {
const dir = STEP_TO_ARROW[stepDir]
const now = performance.now()
setTargetPulse({ dir, key: now })
let best: GameArrow | null = null
let bestDelta = Infinity
@ -183,7 +185,11 @@ export const ArrowGame = forwardRef<ArrowGameHandle, Props>(({ active = true, on
<div className="ag-targets">
{DIRECTIONS.map(dir => (
<div key={dir} className="ag-target" style={{ left: LANE_X[dir] }}>
<div
key={targetPulse?.dir === dir ? `${dir}-${targetPulse.key}` : dir}
className={`ag-target${targetPulse?.dir === dir ? ' ag-target-pulse' : ''}`}
style={{ left: LANE_X[dir] }}
>
<Arrow direction={dir} />
</div>
))}