packages/core/src/features/sprites/formats/aseprite.ts
This is the source snapshot used to build these API details. View this revision on GitHub.
1 import type { SpriteSheetFormat, SpriteFrameData, SpriteAnimationFrameData } from '../model.js';
2 /** Import Aseprite JSON hash or array exports, including frame tags and millisecond timing. See {@link SpriteSheetFormat}.
3 * @returns An explicit atlas metadata adapter.
4 */
5 export function aseprite(): SpriteSheetFormat {
6 return { parse(data: unknown) {
7 const value = data as { frames: Record<string, Entry> | Entry[]; meta: { image: string; frameTags?: { name: string; from: number; to: number; direction?: string }[] } };
8 if (!value?.frames || typeof value.meta?.image !== 'string') throw new TypeError('Expected Aseprite frames and meta.image.');
9 const entries = Array.isArray(value.frames) ? value.frames.map((entry, index) => [entry.filename ?? String(index), entry] as const) : Object.entries(value.frames);
10 const frames: SpriteFrameData[] = entries.map(([name, entry]) => ({ name, rect: { x: entry.frame.x, y: entry.frame.y, width: entry.frame.w, height: entry.frame.h }, sourceSize: entry.sourceSize && { width: entry.sourceSize.w, height: entry.sourceSize.h }, trim: entry.spriteSourceSize && { x: entry.spriteSourceSize.x, y: entry.spriteSourceSize.y }, rotated: entry.rotated }));
11 const animations: Record<string, SpriteAnimationFrameData[]> = Object.create(null);
12 for (const tag of value.meta.frameTags ?? []) {
13 if (!Number.isInteger(tag.from) || !Number.isInteger(tag.to) || tag.from < 0 || tag.to < tag.from || tag.to >= entries.length) throw new RangeError(`Invalid Aseprite tag: ${tag.name}`);
14 let selected = entries.slice(tag.from, tag.to + 1);
15 const direction = tag.direction ?? 'forward';
16 if (!['forward', 'reverse', 'pingpong', 'pingpong_reverse'].includes(direction)) throw new Error(`Unsupported Aseprite direction: ${direction}`);
17 if (direction === 'reverse' || direction === 'pingpong_reverse') selected.reverse();
18 if (direction.startsWith('pingpong')) selected = [...selected, ...selected.slice(1, -1).reverse()];
19 animations[tag.name] = selected.map(([frame, entry]) => ({ frame, duration: entry.duration }));
20 }
21 return { images: [value.meta.image], frames, animations };
22 } };
23 }
24 interface Entry {
25 filename?: string;
26 frame: { x: number; y: number; w: number; h: number };
27 rotated?: boolean;
28 sourceSize?: { w: number; h: number };
29 spriteSourceSize?: { x: number; y: number };
30 duration: number;
31 }
32
Documentation version
Section titled “Documentation version”Documentation built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.