@vitykovskiy/canvas-sprite-animations 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # sprite-animations
2
+
3
+ `sprite-animations` is a framework-agnostic TypeScript library for 2D sprite animation on HTML canvas.
4
+
5
+ The current MVP focus is deliberately narrow:
6
+ - regular grid sprite sheets;
7
+ - runtime playback on `CanvasRenderingContext2D`;
8
+ - timing via `fps` and/or `duration`;
9
+ - predictable positioning and scaling;
10
+ - a separate playground for asset validation and config tuning.
11
+
12
+ ## Current package structure
13
+
14
+ ```text
15
+ src/
16
+ core/
17
+ animation-player.ts
18
+ sprite-sheet.ts
19
+ index.ts
20
+ renderers/
21
+ create-canvas-sprite-renderer.ts
22
+ index.ts
23
+ types.ts
24
+ index.ts
25
+ ```
26
+
27
+ ## Public entry points
28
+
29
+ - `sprite-animations`
30
+ - `sprite-animations/core`
31
+ - `sprite-animations/renderers`
32
+ - `sprite-animations/types`
33
+
34
+ ## Public API draft
35
+
36
+ ```ts
37
+ import {
38
+ createAnimationPlayer,
39
+ createCanvasSpriteRenderer,
40
+ createSpriteSheet,
41
+ loadSpriteSheetImage,
42
+ } from "sprite-animations";
43
+
44
+ const loadedImage = await loadSpriteSheetImage("/assets/hero.png");
45
+
46
+ const spriteSheet = createSpriteSheet({
47
+ image: loadedImage.image,
48
+ grid: {
49
+ frameWidth: 64,
50
+ frameHeight: 64,
51
+ columns: 8,
52
+ rows: 4,
53
+ totalFrames: 24,
54
+ },
55
+ });
56
+
57
+ const player = createAnimationPlayer({
58
+ totalFrames: spriteSheet.getFrameCount(),
59
+ fps: 12,
60
+ duration: 1500,
61
+ loop: true,
62
+ });
63
+
64
+ const renderer = createCanvasSpriteRenderer();
65
+ ```
66
+
67
+ ## API responsibilities
68
+
69
+ - `loadSpriteSheetImage(...)`
70
+ Loads a sprite sheet from a URL, `HTMLImageElement`, or `ImageBitmap`.
71
+
72
+ - `createSpriteSheet(...)`
73
+ Defines the sprite grid contract and frame lookup API.
74
+
75
+ - `createAnimationPlayer(...)`
76
+ Owns playback state and timing policy.
77
+
78
+ - `createCanvasSpriteRenderer(...)`
79
+ Draws a resolved frame to a canvas context without owning playback state.
80
+
81
+ ## Timing policy
82
+
83
+ - If only `fps` is set, playback advances frame-by-frame using frame duration.
84
+ - If only `duration` is set, frames are resolved from elapsed progress across the full animation.
85
+ - If both `fps` and `duration` are set, `duration` has priority and `fps` acts as an update-frequency cap, which means frame skipping is allowed.
86
+
87
+ ## Canvas rendering contract
88
+
89
+ - The renderer only draws a requested frame.
90
+ - Positioning is controlled through `{ x, y }` draw options.
91
+ - Scaling is applied at draw time and does not mutate sprite sheet metadata.
92
+
93
+ ## Status
94
+
95
+ This repository is in active MVP setup. The package structure and public API are defined first; asset loading, runtime behavior hardening, playground UX, and publish-ready infrastructure are tracked as separate backlog tasks.
96
+
97
+ ## Validation
98
+
99
+ Current scaffold validation commands:
100
+
101
+ ```bash
102
+ tsc --noEmit -p tsconfig.json
103
+ npm test
104
+ npm run playground:build
105
+ npm pack --dry-run
106
+ ```
107
+
108
+ ## Playground
109
+
110
+ Run the dev playground locally:
111
+
112
+ ```bash
113
+ npm install
114
+ npm run playground:dev
115
+ ```
116
+
117
+ ## Package workflow
118
+
119
+ - `npm run build`
120
+ Builds library artifacts into `dist/` without bundling the playground.
121
+
122
+ - `npm test`
123
+ Rebuilds the library and runs runtime regression tests.
124
+
125
+ - `npm run check`
126
+ Runs typecheck, tests, and playground build as a single quality gate.
127
+
128
+ - `npm run pack:check`
129
+ Verifies the package contents with `npm pack --dry-run`.
130
+
131
+ - `npm run playground:build`
132
+ Emits the dev playground into `playground-dist/` so it does not overwrite library artifacts in `dist/`.
@@ -0,0 +1,3 @@
1
+ import type { AnimationPlayer, AnimationPlayerConfig } from "../types.js";
2
+ export declare function createAnimationPlayer(config: AnimationPlayerConfig): AnimationPlayer;
3
+ //# sourceMappingURL=animation-player.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animation-player.d.ts","sourceRoot":"","sources":["../../src/core/animation-player.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,qBAAqB,EAGtB,MAAM,aAAa,CAAC;AAErB,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,qBAAqB,GAC5B,eAAe,CAoGjB"}
@@ -0,0 +1,118 @@
1
+ export function createAnimationPlayer(config) {
2
+ const normalizedConfig = normalizeAnimationPlayerConfig(config);
3
+ let playbackState = "idle";
4
+ let elapsedMs = 0;
5
+ let frameIndex = normalizedConfig.initialFrame;
6
+ let completed = false;
7
+ const snapshot = () => ({
8
+ frameIndex,
9
+ elapsedMs,
10
+ playbackState,
11
+ completed,
12
+ });
13
+ return {
14
+ config: toPublicAnimationPlayerConfig(normalizedConfig),
15
+ play() {
16
+ playbackState = "playing";
17
+ completed = false;
18
+ },
19
+ pause() {
20
+ if (playbackState === "playing") {
21
+ playbackState = "paused";
22
+ }
23
+ },
24
+ stop() {
25
+ playbackState = "stopped";
26
+ elapsedMs = 0;
27
+ frameIndex = normalizedConfig.initialFrame;
28
+ completed = false;
29
+ },
30
+ reset() {
31
+ playbackState = "idle";
32
+ elapsedMs = 0;
33
+ frameIndex = normalizedConfig.initialFrame;
34
+ completed = false;
35
+ },
36
+ update(deltaMs) {
37
+ if (playbackState !== "playing" || deltaMs <= 0) {
38
+ return snapshot();
39
+ }
40
+ elapsedMs += deltaMs;
41
+ if (normalizedConfig.duration !== undefined) {
42
+ const duration = normalizedConfig.duration;
43
+ if (duration <= 0) {
44
+ frameIndex = normalizedConfig.totalFrames - 1;
45
+ completed = true;
46
+ playbackState = normalizedConfig.loop ? "playing" : "stopped";
47
+ return snapshot();
48
+ }
49
+ const cycles = elapsedMs / duration;
50
+ if (!normalizedConfig.loop && cycles >= 1) {
51
+ frameIndex = normalizedConfig.totalFrames - 1;
52
+ elapsedMs = duration;
53
+ completed = true;
54
+ playbackState = "stopped";
55
+ return snapshot();
56
+ }
57
+ const cycleProgress = normalizedConfig.loop
58
+ ? cycles - Math.floor(cycles)
59
+ : Math.min(cycles, 0.999999);
60
+ frameIndex = Math.min(normalizedConfig.totalFrames - 1, Math.floor(cycleProgress * normalizedConfig.totalFrames));
61
+ return snapshot();
62
+ }
63
+ const frameDurationMs = 1000 / normalizedConfig.fps;
64
+ const nextFrameIndex = normalizedConfig.initialFrame +
65
+ Math.floor(elapsedMs / frameDurationMs);
66
+ if (normalizedConfig.loop) {
67
+ frameIndex = nextFrameIndex % normalizedConfig.totalFrames;
68
+ return snapshot();
69
+ }
70
+ if (nextFrameIndex >= normalizedConfig.totalFrames) {
71
+ frameIndex = normalizedConfig.totalFrames - 1;
72
+ completed = true;
73
+ playbackState = "stopped";
74
+ return snapshot();
75
+ }
76
+ frameIndex = nextFrameIndex;
77
+ return snapshot();
78
+ },
79
+ getSnapshot() {
80
+ return snapshot();
81
+ },
82
+ };
83
+ }
84
+ function normalizeAnimationPlayerConfig(config) {
85
+ if (!Number.isInteger(config.totalFrames) || config.totalFrames <= 0) {
86
+ throw new Error("Animation player requires a positive integer totalFrames.");
87
+ }
88
+ if (config.fps !== undefined && config.fps <= 0) {
89
+ throw new Error("fps must be greater than 0 when provided.");
90
+ }
91
+ if (config.duration !== undefined && config.duration < 0) {
92
+ throw new Error("duration must be greater than or equal to 0 when provided.");
93
+ }
94
+ const initialFrame = config.initialFrame ?? 0;
95
+ if (!Number.isInteger(initialFrame) || initialFrame < 0) {
96
+ throw new Error("initialFrame must be a non-negative integer.");
97
+ }
98
+ if (initialFrame >= config.totalFrames) {
99
+ throw new Error("initialFrame must be less than totalFrames.");
100
+ }
101
+ return Object.freeze({
102
+ totalFrames: config.totalFrames,
103
+ initialFrame,
104
+ fps: config.fps ?? 12,
105
+ duration: config.duration,
106
+ loop: config.loop ?? true,
107
+ });
108
+ }
109
+ function toPublicAnimationPlayerConfig(config) {
110
+ return Object.freeze({
111
+ totalFrames: config.totalFrames,
112
+ initialFrame: config.initialFrame,
113
+ fps: config.fps,
114
+ ...(config.duration === undefined ? {} : { duration: config.duration }),
115
+ loop: config.loop,
116
+ });
117
+ }
118
+ //# sourceMappingURL=animation-player.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animation-player.js","sourceRoot":"","sources":["../../src/core/animation-player.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { LoadedSpriteSheetImage, LoadSpriteSheetImageOptions, SpriteSheetImageSource } from "../types.js";
2
+ export declare function loadSpriteSheetImage(source: SpriteSheetImageSource, options?: LoadSpriteSheetImageOptions): Promise<LoadedSpriteSheetImage>;
3
+ //# sourceMappingURL=asset-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-loader.d.ts","sourceRoot":"","sources":["../../src/core/asset-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,2BAA2B,EAC3B,sBAAsB,EACvB,MAAM,aAAa,CAAC;AAErB,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,sBAAsB,EAC9B,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,sBAAsB,CAAC,CAoBjC"}
@@ -0,0 +1,56 @@
1
+ export async function loadSpriteSheetImage(source, options = {}) {
2
+ if (typeof source === "string") {
3
+ return loadImageFromUrl(source, options);
4
+ }
5
+ if (isImageBitmap(source)) {
6
+ return {
7
+ image: source,
8
+ width: source.width,
9
+ height: source.height,
10
+ };
11
+ }
12
+ await waitForImageElement(source);
13
+ return {
14
+ image: source,
15
+ width: source.naturalWidth,
16
+ height: source.naturalHeight,
17
+ };
18
+ }
19
+ async function loadImageFromUrl(url, options) {
20
+ const image = new Image();
21
+ if (options.crossOrigin !== undefined) {
22
+ image.crossOrigin = options.crossOrigin;
23
+ }
24
+ image.src = url;
25
+ await waitForImageElement(image);
26
+ return {
27
+ image,
28
+ width: image.naturalWidth,
29
+ height: image.naturalHeight,
30
+ };
31
+ }
32
+ function waitForImageElement(image) {
33
+ if (image.complete && image.naturalWidth > 0) {
34
+ return Promise.resolve();
35
+ }
36
+ return new Promise((resolve, reject) => {
37
+ const handleLoad = () => {
38
+ cleanup();
39
+ resolve();
40
+ };
41
+ const handleError = () => {
42
+ cleanup();
43
+ reject(new Error("Failed to load sprite sheet image."));
44
+ };
45
+ const cleanup = () => {
46
+ image.removeEventListener("load", handleLoad);
47
+ image.removeEventListener("error", handleError);
48
+ };
49
+ image.addEventListener("load", handleLoad, { once: true });
50
+ image.addEventListener("error", handleError, { once: true });
51
+ });
52
+ }
53
+ function isImageBitmap(value) {
54
+ return typeof ImageBitmap !== "undefined" && value instanceof ImageBitmap;
55
+ }
56
+ //# sourceMappingURL=asset-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-loader.js","sourceRoot":"","sources":["../../src/core/asset-loader.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAA8B,EAC9B,UAAuC,EAAE;IAEzC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,KAAK,EAAE,MAAM;YACb,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,MAAM,CAAC,MAAM;SACtB,CAAC;IACJ,CAAC;IAED,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAElC,OAAO;QACL,KAAK,EAAE,MAAM;QACb,KAAK,EAAE,MAAM,CAAC,YAAY;QAC1B,MAAM,EAAE,MAAM,CAAC,aAAa;KAC7B,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,GAAW,EACX,OAAoC;IAEpC,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC;IAE1B,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QACtC,KAAK,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC;IAEhB,MAAM,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAEjC,OAAO;QACL,KAAK;QACL,KAAK,EAAE,KAAK,CAAC,YAAY;QACzB,MAAM,EAAE,KAAK,CAAC,aAAa;KAC5B,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAuB;IAClD,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;QAC7C,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,UAAU,GAAG,GAAG,EAAE;YACtB,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QAEF,MAAM,WAAW,GAAG,GAAG,EAAE;YACvB,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC,CAAC;QAC1D,CAAC,CAAC;QAEF,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,KAAK,CAAC,mBAAmB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;YAC9C,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAClD,CAAC,CAAC;QAEF,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,aAAa,CAAC,KAA6B;IAClD,OAAO,OAAO,WAAW,KAAK,WAAW,IAAI,KAAK,YAAY,WAAW,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { createSpriteSheet } from "./sprite-sheet.js";
2
+ export { createAnimationPlayer } from "./animation-player.js";
3
+ export { loadSpriteSheetImage } from "./asset-loader.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { createSpriteSheet } from "./sprite-sheet.js";
2
+ export { createAnimationPlayer } from "./animation-player.js";
3
+ export { loadSpriteSheetImage } from "./asset-loader.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { SpriteSheet, SpriteSheetConfig } from "../types.js";
2
+ export declare function createSpriteSheet(config: SpriteSheetConfig): SpriteSheet;
3
+ //# sourceMappingURL=sprite-sheet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sprite-sheet.d.ts","sourceRoot":"","sources":["../../src/core/sprite-sheet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE7E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAaxE"}
@@ -0,0 +1,57 @@
1
+ export function createSpriteSheet(config) {
2
+ const normalizedGrid = normalizeGrid(config);
3
+ return {
4
+ image: config.image,
5
+ grid: normalizedGrid,
6
+ getFrameCount() {
7
+ return normalizedGrid.totalFrames;
8
+ },
9
+ getFrameRect(frameIndex) {
10
+ return getFrameRect(normalizedGrid, frameIndex);
11
+ },
12
+ };
13
+ }
14
+ function normalizeGrid(config) {
15
+ const { frameWidth, frameHeight, columns, rows } = config.grid;
16
+ if (frameWidth <= 0 || frameHeight <= 0) {
17
+ throw new Error("frameWidth and frameHeight must be greater than 0.");
18
+ }
19
+ if (!Number.isInteger(columns) || columns <= 0) {
20
+ throw new Error("columns must be a positive integer.");
21
+ }
22
+ if (!Number.isInteger(rows) || rows <= 0) {
23
+ throw new Error("rows must be a positive integer.");
24
+ }
25
+ const totalFrames = config.grid.totalFrames ?? columns * rows;
26
+ if (!Number.isInteger(totalFrames) || totalFrames <= 0) {
27
+ throw new Error("totalFrames must be a positive integer when provided.");
28
+ }
29
+ if (totalFrames > columns * rows) {
30
+ throw new Error("totalFrames cannot exceed the capacity of the sprite sheet grid.");
31
+ }
32
+ return {
33
+ frameWidth,
34
+ frameHeight,
35
+ columns,
36
+ rows,
37
+ totalFrames,
38
+ };
39
+ }
40
+ function getFrameRect(grid, frameIndex) {
41
+ if (!Number.isInteger(frameIndex) || frameIndex < 0) {
42
+ throw new Error("frameIndex must be a non-negative integer.");
43
+ }
44
+ if (frameIndex >= grid.totalFrames) {
45
+ throw new Error("frameIndex is outside the configured totalFrames range.");
46
+ }
47
+ const columnIndex = frameIndex % grid.columns;
48
+ const rowIndex = Math.floor(frameIndex / grid.columns);
49
+ return {
50
+ x: columnIndex * grid.frameWidth,
51
+ y: rowIndex * grid.frameHeight,
52
+ width: grid.frameWidth,
53
+ height: grid.frameHeight,
54
+ index: frameIndex,
55
+ };
56
+ }
57
+ //# sourceMappingURL=sprite-sheet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sprite-sheet.js","sourceRoot":"","sources":["../../src/core/sprite-sheet.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,iBAAiB,CAAC,MAAyB;IACzD,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAE7C,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,IAAI,EAAE,cAAc;QACpB,aAAa;YACX,OAAO,cAAc,CAAC,WAAW,CAAC;QACpC,CAAC;QACD,YAAY,CAAC,UAAU;YACrB,OAAO,YAAY,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAClD,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,MAAyB;IAC9C,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IAE/D,IAAI,UAAU,IAAI,CAAC,IAAI,WAAW,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,IAAI,OAAO,GAAG,IAAI,CAAC;IAE9D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,WAAW,IAAI,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IAED,IAAI,WAAW,GAAG,OAAO,GAAG,IAAI,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;IACtF,CAAC;IAED,OAAO;QACL,UAAU;QACV,WAAW;QACX,OAAO;QACP,IAAI;QACJ,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CACnB,IAAyC,EACzC,UAAkB;IAElB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,UAAU,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,WAAW,GAAG,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IAEvD,OAAO;QACL,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC,UAAU;QAChC,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC,WAAW;QAC9B,KAAK,EAAE,IAAI,CAAC,UAAU;QACtB,MAAM,EAAE,IAAI,CAAC,WAAW;QACxB,KAAK,EAAE,UAAU;KAClB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from "./core/index.js";
2
+ export * from "./renderers/index.js";
3
+ export type * from "./types.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,mBAAmB,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./core/index.js";
2
+ export * from "./renderers/index.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CanvasSpriteRenderer } from "../types.js";
2
+ export declare function createCanvasSpriteRenderer(): CanvasSpriteRenderer;
3
+ //# sourceMappingURL=create-canvas-sprite-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-canvas-sprite-renderer.d.ts","sourceRoot":"","sources":["../../src/renderers/create-canvas-sprite-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EAGrB,MAAM,aAAa,CAAC;AAErB,wBAAgB,0BAA0B,IAAI,oBAAoB,CA0BjE"}
@@ -0,0 +1,12 @@
1
+ export function createCanvasSpriteRenderer() {
2
+ return {
3
+ draw(context, spriteSheet, options) {
4
+ const frame = spriteSheet.getFrameRect(options.frameIndex);
5
+ const scale = options.scale ?? 1;
6
+ const destinationWidth = options.destinationWidth ?? frame.width * scale;
7
+ const destinationHeight = options.destinationHeight ?? frame.height * scale;
8
+ context.drawImage(spriteSheet.image, frame.x, frame.y, frame.width, frame.height, options.position.x, options.position.y, destinationWidth, destinationHeight);
9
+ },
10
+ };
11
+ }
12
+ //# sourceMappingURL=create-canvas-sprite-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-canvas-sprite-renderer.js","sourceRoot":"","sources":["../../src/renderers/create-canvas-sprite-renderer.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,0BAA0B;IACxC,OAAO;QACL,IAAI,CACF,OAAiC,EACjC,WAAwB,EACxB,OAA0B;YAE1B,MAAM,KAAK,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC3D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC;YACjC,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;YACzE,MAAM,iBAAiB,GACrB,OAAO,CAAC,iBAAiB,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;YAEpD,OAAO,CAAC,SAAS,CACf,WAAW,CAAC,KAAK,EACjB,KAAK,CAAC,CAAC,EACP,KAAK,CAAC,CAAC,EACP,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,MAAM,EACZ,OAAO,CAAC,QAAQ,CAAC,CAAC,EAClB,OAAO,CAAC,QAAQ,CAAC,CAAC,EAClB,gBAAgB,EAChB,iBAAiB,CAClB,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { createCanvasSpriteRenderer } from "./create-canvas-sprite-renderer.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/renderers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { createCanvasSpriteRenderer } from "./create-canvas-sprite-renderer.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/renderers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC"}
@@ -0,0 +1,73 @@
1
+ export interface SpriteSheetGrid {
2
+ frameWidth: number;
3
+ frameHeight: number;
4
+ columns: number;
5
+ rows: number;
6
+ totalFrames?: number;
7
+ }
8
+ export type SpriteSheetImageSource = string | HTMLImageElement | ImageBitmap;
9
+ export interface LoadSpriteSheetImageOptions {
10
+ crossOrigin?: string;
11
+ }
12
+ export interface LoadedSpriteSheetImage {
13
+ image: HTMLImageElement | ImageBitmap;
14
+ width: number;
15
+ height: number;
16
+ }
17
+ export interface SpriteSheetConfig {
18
+ image: CanvasImageSource;
19
+ grid: SpriteSheetGrid;
20
+ }
21
+ export interface FrameRect {
22
+ x: number;
23
+ y: number;
24
+ width: number;
25
+ height: number;
26
+ index: number;
27
+ }
28
+ export interface SpriteSheet {
29
+ readonly image: CanvasImageSource;
30
+ readonly grid: Required<SpriteSheetGrid>;
31
+ getFrameCount(): number;
32
+ getFrameRect(frameIndex: number): FrameRect;
33
+ }
34
+ export interface AnimationTimingConfig {
35
+ fps?: number;
36
+ duration?: number;
37
+ loop?: boolean;
38
+ }
39
+ export interface AnimationPlayerConfig extends AnimationTimingConfig {
40
+ totalFrames: number;
41
+ initialFrame?: number;
42
+ }
43
+ export type AnimationPlaybackState = "idle" | "playing" | "paused" | "stopped";
44
+ export interface AnimationSnapshot {
45
+ frameIndex: number;
46
+ elapsedMs: number;
47
+ playbackState: AnimationPlaybackState;
48
+ completed: boolean;
49
+ }
50
+ export interface AnimationPlayer {
51
+ readonly config: Readonly<AnimationPlayerConfig>;
52
+ play(): void;
53
+ pause(): void;
54
+ stop(): void;
55
+ reset(): void;
56
+ update(deltaMs: number): AnimationSnapshot;
57
+ getSnapshot(): AnimationSnapshot;
58
+ }
59
+ export interface RenderPosition {
60
+ x: number;
61
+ y: number;
62
+ }
63
+ export interface DrawSpriteOptions {
64
+ scale?: number;
65
+ position: RenderPosition;
66
+ frameIndex: number;
67
+ destinationWidth?: number;
68
+ destinationHeight?: number;
69
+ }
70
+ export interface CanvasSpriteRenderer {
71
+ draw(context: CanvasRenderingContext2D, spriteSheet: SpriteSheet, options: DrawSpriteOptions): void;
72
+ }
73
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,gBAAgB,GAAG,WAAW,CAAC;AAE7E,MAAM,WAAW,2BAA2B;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,gBAAgB,GAAG,WAAW,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,iBAAiB,CAAC;IACzB,IAAI,EAAE,eAAe,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IACzC,aAAa,IAAI,MAAM,CAAC;IACxB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,qBAAsB,SAAQ,qBAAqB;IAClE,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE/E,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,sBAAsB,CAAC;IACtC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC,CAAC;IACjD,IAAI,IAAI,IAAI,CAAC;IACb,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,IAAI,IAAI,CAAC;IACb,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAAC;IAC3C,WAAW,IAAI,iBAAiB,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,cAAc,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CACF,OAAO,EAAE,wBAAwB,EACjC,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,iBAAiB,GACzB,IAAI,CAAC;CACT"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@vitykovskiy/canvas-sprite-animations",
3
+ "version": "0.1.0",
4
+ "description": "Framework-agnostic sprite animation library for HTML canvas.",
5
+ "type": "module",
6
+ "private": false,
7
+ "packageManager": "npm@11.6.1",
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "sideEffects": false,
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ },
20
+ "./core": {
21
+ "types": "./dist/core/index.d.ts",
22
+ "import": "./dist/core/index.js"
23
+ },
24
+ "./renderers": {
25
+ "types": "./dist/renderers/index.d.ts",
26
+ "import": "./dist/renderers/index.js"
27
+ },
28
+ "./types": {
29
+ "types": "./dist/types.d.ts",
30
+ "import": "./dist/types.js"
31
+ }
32
+ },
33
+ "scripts": {
34
+ "clean": "node -e \"import('node:fs/promises').then(fs => fs.rm('dist', { recursive: true, force: true }))\"",
35
+ "typecheck": "tsc --noEmit -p tsconfig.json",
36
+ "build": "npm run clean && tsc -p tsconfig.build.json",
37
+ "build:types": "tsc -p tsconfig.build.json",
38
+ "test": "npm run build && node --test tests/**/*.test.mjs",
39
+ "playground:dev": "vite",
40
+ "playground:build": "vite build --outDir playground-dist",
41
+ "check": "npm run typecheck && npm test && npm run playground:build",
42
+ "pack:check": "npm pack --dry-run",
43
+ "prepack": "npm run build"
44
+ },
45
+ "keywords": [
46
+ "sprite",
47
+ "animation",
48
+ "canvas",
49
+ "typescript"
50
+ ],
51
+ "license": "MIT",
52
+ "repository": {
53
+ "type": "git",
54
+ "url": "git+https://github.com/Vitykovskiy/sprite-animations.git"
55
+ },
56
+ "homepage": "https://github.com/Vitykovskiy/sprite-animations",
57
+ "bugs": {
58
+ "url": "https://github.com/Vitykovskiy/sprite-animations/issues"
59
+ },
60
+ "engines": {
61
+ "node": ">=22.0.0"
62
+ },
63
+ "devDependencies": {
64
+ "typescript": "^5.8.3",
65
+ "vite": "^7.1.5"
66
+ }
67
+ }