feat: improve dev tool UX
- Show uploaded spritesheet image - Auto-calculate frame dimensions from image size + frame count - Add scale slider (1x-8x) for preview size 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d3e1eb00bd
commit
b1a87662fb
|
|
@ -3,21 +3,32 @@ import { render } from "hono/jsx/dom"
|
|||
|
||||
const App = () => {
|
||||
const [spriteUrl, setSpriteUrl] = useState("")
|
||||
const [width, setWidth] = useState(32)
|
||||
const [height, setHeight] = useState(32)
|
||||
const [imageWidth, setImageWidth] = useState(0)
|
||||
const [imageHeight, setImageHeight] = useState(0)
|
||||
const [frames, setFrames] = useState(4)
|
||||
const [columns, setColumns] = useState<number | undefined>()
|
||||
const [frameDuration, setFrameDuration] = useState(100)
|
||||
const [scale, setScale] = useState(1)
|
||||
|
||||
const handleFileChange = (e: Event) => {
|
||||
const file = (e.target as HTMLInputElement).files?.[0]
|
||||
if (!file) return
|
||||
setSpriteUrl(URL.createObjectURL(file))
|
||||
const url = URL.createObjectURL(file)
|
||||
const img = new Image()
|
||||
img.onload = () => {
|
||||
setImageWidth(img.width)
|
||||
setImageHeight(img.height)
|
||||
setSpriteUrl(url)
|
||||
}
|
||||
img.src = url
|
||||
}
|
||||
|
||||
// Auto-calculate frame dimensions from image size and frame count
|
||||
const isGrid = columns !== undefined
|
||||
const cols = isGrid ? columns : frames
|
||||
const rows = isGrid ? Math.ceil(frames / columns!) : 1
|
||||
const width = imageWidth > 0 ? Math.floor(imageWidth / cols) : 32
|
||||
const height = imageHeight > 0 ? Math.floor(imageHeight / rows) : 32
|
||||
const sheetWidth = cols * width
|
||||
const sheetHeight = rows * height
|
||||
const totalDuration = frames * frameDuration
|
||||
|
|
@ -41,10 +52,10 @@ const App = () => {
|
|||
}
|
||||
|
||||
const previewStyle = {
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
width: `${width * scale}px`,
|
||||
height: `${height * scale}px`,
|
||||
backgroundImage: `url('${spriteUrl}')`,
|
||||
backgroundSize: `${sheetWidth}px ${sheetHeight}px`,
|
||||
backgroundSize: `${sheetWidth * scale}px ${sheetHeight * scale}px`,
|
||||
animation: `${keyframeId} ${totalDuration}ms steps(${frames}) infinite`,
|
||||
}
|
||||
|
||||
|
|
@ -66,14 +77,12 @@ const App = () => {
|
|||
Spritesheet
|
||||
<input type="file" accept="image/*" onChange={handleFileChange} />
|
||||
</label>
|
||||
<label>
|
||||
Frame Width (px)
|
||||
<input type="number" value={width} min={1} onInput={(e) => setWidth(+(e.target as HTMLInputElement).value)} />
|
||||
</label>
|
||||
<label>
|
||||
Frame Height (px)
|
||||
<input type="number" value={height} min={1} onInput={(e) => setHeight(+(e.target as HTMLInputElement).value)} />
|
||||
</label>
|
||||
{spriteUrl && (
|
||||
<div style={{ marginBottom: "1rem" }}>
|
||||
<small>Spritesheet ({imageWidth} x {imageHeight})</small>
|
||||
<img src={spriteUrl} style={{ maxWidth: "100%", imageRendering: "pixelated" }} />
|
||||
</div>
|
||||
)}
|
||||
<label>
|
||||
Frame Count
|
||||
<input type="number" value={frames} min={1} onInput={(e) => setFrames(+(e.target as HTMLInputElement).value)} />
|
||||
|
|
@ -89,6 +98,13 @@ const App = () => {
|
|||
Frame Duration (ms)
|
||||
<input type="number" value={frameDuration} min={1} onInput={(e) => setFrameDuration(+(e.target as HTMLInputElement).value)} />
|
||||
</label>
|
||||
<label>
|
||||
Scale ({scale}x)
|
||||
<input type="range" value={scale} min={1} max={8} step={1} onInput={(e) => setScale(+(e.target as HTMLInputElement).value)} />
|
||||
</label>
|
||||
{spriteUrl && (
|
||||
<small>Frame size: {width} x {height} px</small>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div class="preview-area">
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user