claude-code-best/packages/pokemon/src/ui/EvolutionAnim.tsx
2026-04-21 21:38:13 +08:00

91 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react'
import { Box, Text, type Color } from '@anthropic/ink'
import type { SpeciesId } from '../types'
import { getSpeciesData } from '../data/species'
import { loadSprite } from '../core/spriteCache'
import { getFallbackSprite } from '../sprites/fallback'
const YELLOW: Color = 'ansi:yellow'
const GREEN: Color = 'ansi:green'
const GRAY: Color = 'ansi:white'
interface EvolutionAnimProps {
fromSpecies: SpeciesId
toSpecies: SpeciesId
onComplete: () => void
}
/**
* Evolution animation component.
* Displays a flashing/morphing effect from old species to new species.
* 8 frames × 500ms = ~4 seconds total.
*/
export function EvolutionAnim({ fromSpecies, toSpecies, onComplete }: EvolutionAnimProps) {
const [tick, setTick] = useState(0)
const totalFrames = 8
useEffect(() => {
if (tick >= totalFrames) {
onComplete()
return
}
const timer = setTimeout(() => setTick((t) => t + 1), 500)
return () => clearTimeout(timer)
}, [tick, onComplete])
const fromSprite = getSpriteLines(fromSpecies)
const toSprite = getSpriteLines(toSpecies)
const fromName = getSpeciesData(fromSpecies).name
const toName = getSpeciesData(toSpecies).name
// Frame logic:
// 0-3: old sprite with flash (alternate blank)
// 4-7: alternate old/new, settle on new
let displayLines: string[]
if (tick < 3) {
displayLines = tick % 2 === 0 ? fromSprite : fromSprite.map(() => '')
} else if (tick < 6) {
displayLines = tick % 2 === 0 ? fromSprite : toSprite
} else {
displayLines = toSprite
}
return (
<Box flexDirection="column" borderStyle="round" paddingX={1} alignItems="center">
<Text bold color={YELLOW}>
Evolution!
</Text>
<Box flexDirection="column" alignItems="center" marginY={1}>
{displayLines.map((line, i) => (
<Text key={i}>
{tick >= 6 ? '✨ ' : ''}
{line}
{tick >= 6 ? ' ✨' : ''}
</Text>
))}
</Box>
<Text>
<Text color={GRAY}>{fromName}</Text>
<Text color={YELLOW}> </Text>
<Text bold color={GREEN}>
{toName}
</Text>
</Text>
{tick >= totalFrames - 1 && (
<Text bold color={GREEN}>
</Text>
)}
</Box>
)
}
function getSpriteLines(speciesId: SpeciesId): string[] {
const cached = loadSprite(speciesId)
if (cached) return cached.lines
return getFallbackSprite(speciesId)
}