Skip to content

packages/core/src/features/sprites/formats/aseprite.ts

Read as Markdown

This is the source snapshot used to build these API details. View this revision on GitHub.

Back to reference

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 built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.