mason-sprite 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.
@@ -0,0 +1,205 @@
1
+ import { defineComponent as F, ref as w, onMounted as T, onBeforeUnmount as P, watch as k, openBlock as c, createElementBlock as u, normalizeStyle as d, normalizeClass as m, mergeDefaults as C } from "vue";
2
+ const p = {
3
+ fps: 12,
4
+ loop: !0,
5
+ width: 128,
6
+ height: 128,
7
+ autoPlay: !0,
8
+ renderer: "css"
9
+ };
10
+ function S(i, t) {
11
+ return i * t;
12
+ }
13
+ function f(i, t) {
14
+ return {
15
+ row: Math.floor(i / t),
16
+ col: i % t
17
+ };
18
+ }
19
+ function I(i, t, r) {
20
+ const { row: e, col: o } = f(i, r), n = r <= 1 ? 0 : o / (r - 1) * 100, s = t <= 1 ? 0 : e / (t - 1) * 100;
21
+ return { x: n, y: s };
22
+ }
23
+ function x(i, t, r, e, o, n, s) {
24
+ const h = i.getContext("2d");
25
+ if (!h) return;
26
+ const a = t.naturalWidth / o, l = t.naturalHeight / e, { row: g, col: y } = f(r, o);
27
+ i.width = n, i.height = s, h.clearRect(0, 0, n, s), h.drawImage(
28
+ t,
29
+ y * a,
30
+ g * l,
31
+ a,
32
+ l,
33
+ 0,
34
+ 0,
35
+ n,
36
+ s
37
+ );
38
+ }
39
+ function v(i, t, r, e, o, n, s) {
40
+ const { x: h, y: a } = I(r, e, o);
41
+ i.style.backgroundImage = `url("${t}")`, i.style.backgroundRepeat = "no-repeat", i.style.backgroundSize = `${o * 100}% ${e * 100}%`, i.style.backgroundPosition = `${h}% ${a}%`, i.style.width = `${n}px`, i.style.height = `${s}px`, i.style.display = "inline-block";
42
+ }
43
+ function $(i) {
44
+ i.style.backgroundImage = "";
45
+ }
46
+ class L {
47
+ constructor(t) {
48
+ this.currentFrame = 0, this.isPlaying = !1, this.isLoaded = !1, this.rafId = null, this.lastTimestamp = 0, this.accumulatedTime = 0, this.image = null, this.target = null, this.listeners = /* @__PURE__ */ new Set(), this.destroyed = !1, this.tick = (r) => {
49
+ if (!this.isPlaying || this.destroyed) return;
50
+ this.lastTimestamp === 0 && (this.lastTimestamp = r);
51
+ const e = r - this.lastTimestamp;
52
+ this.lastTimestamp = r, this.accumulatedTime += e;
53
+ const o = 1e3 / this.options.fps;
54
+ for (; this.accumulatedTime >= o; )
55
+ this.accumulatedTime -= o, this.advanceFrame();
56
+ this.rafId = requestAnimationFrame(this.tick);
57
+ }, this.options = {
58
+ ...p,
59
+ ...t
60
+ }, this.loadImage();
61
+ }
62
+ attach(t) {
63
+ this.target = t, this.isLoaded && this.render(), this.options.autoPlay && this.play();
64
+ }
65
+ play() {
66
+ this.destroyed || this.isPlaying || (this.isPlaying = !0, this.lastTimestamp = 0, this.accumulatedTime = 0, this.rafId = requestAnimationFrame(this.tick), this.notify());
67
+ }
68
+ pause() {
69
+ this.isPlaying && (this.isPlaying = !1, this.rafId !== null && (cancelAnimationFrame(this.rafId), this.rafId = null), this.notify());
70
+ }
71
+ stop() {
72
+ this.pause(), this.currentFrame = 0, this.render(), this.notify();
73
+ }
74
+ goToFrame(t) {
75
+ var e, o;
76
+ const r = this.getTotalFrames();
77
+ this.currentFrame = Math.max(0, Math.min(t, r - 1)), this.render(), (o = (e = this.options).onFrameChange) == null || o.call(e, this.currentFrame), this.notify();
78
+ }
79
+ getState() {
80
+ return {
81
+ currentFrame: this.currentFrame,
82
+ totalFrames: this.getTotalFrames(),
83
+ isPlaying: this.isPlaying,
84
+ isLoaded: this.isLoaded
85
+ };
86
+ }
87
+ subscribe(t) {
88
+ return this.listeners.add(t), t(this.getState()), () => this.listeners.delete(t);
89
+ }
90
+ updateOptions(t) {
91
+ const r = this.options.src, e = this.options.fps;
92
+ this.options = { ...this.options, ...t }, t.src !== void 0 && t.src !== r ? this.loadImage() : this.isLoaded && this.render(), t.fps !== void 0 && t.fps !== e && (this.accumulatedTime = 0);
93
+ }
94
+ destroy() {
95
+ this.destroyed = !0, this.pause(), this.listeners.clear(), this.target && this.options.renderer === "css" && $(this.target), this.target = null, this.image = null;
96
+ }
97
+ getTotalFrames() {
98
+ return S(this.options.rows, this.options.cols);
99
+ }
100
+ loadImage() {
101
+ this.isLoaded = !1;
102
+ const t = new Image();
103
+ t.crossOrigin = "anonymous", t.onload = () => {
104
+ if (!this.destroyed) {
105
+ if (this.image = t, this.isLoaded = !0, !this.options.width || !this.options.height) {
106
+ const r = t.naturalWidth / this.options.cols, e = t.naturalHeight / this.options.rows;
107
+ this.options.width = r, this.options.height = e;
108
+ }
109
+ this.render(), this.notify(), this.options.autoPlay && this.target && this.play();
110
+ }
111
+ }, t.onerror = () => {
112
+ console.error(`[SpriteAnimator] Failed to load image: ${this.options.src}`);
113
+ }, t.src = this.options.src;
114
+ }
115
+ advanceFrame() {
116
+ var e, o, n, s;
117
+ const t = this.getTotalFrames(), r = this.currentFrame + 1;
118
+ r >= t ? this.options.loop ? this.currentFrame = 0 : (this.currentFrame = t - 1, this.pause(), (o = (e = this.options).onComplete) == null || o.call(e)) : this.currentFrame = r, this.render(), (s = (n = this.options).onFrameChange) == null || s.call(n, this.currentFrame), this.notify();
119
+ }
120
+ render() {
121
+ if (!this.target || !this.isLoaded) return;
122
+ const { src: t, rows: r, cols: e, width: o, height: n, renderer: s } = this.options;
123
+ s === "canvas" && this.target instanceof HTMLCanvasElement && this.image ? x(this.target, this.image, this.currentFrame, r, e, o, n) : s === "css" && this.target instanceof HTMLElement && v(this.target, t, this.currentFrame, r, e, o, n);
124
+ }
125
+ notify() {
126
+ const t = this.getState();
127
+ this.listeners.forEach((r) => r(t));
128
+ }
129
+ }
130
+ const A = /* @__PURE__ */ F({
131
+ __name: "Sprite",
132
+ props: /* @__PURE__ */ C({
133
+ src: {},
134
+ rows: {},
135
+ cols: {},
136
+ fps: {},
137
+ loop: { type: Boolean },
138
+ width: {},
139
+ height: {},
140
+ autoPlay: { type: Boolean },
141
+ renderer: {},
142
+ onComplete: { type: Function },
143
+ onFrameChange: { type: Function },
144
+ class: {}
145
+ }, p),
146
+ emits: ["complete", "frameChange"],
147
+ setup(i, { expose: t, emit: r }) {
148
+ const e = i, o = r, n = w(null);
149
+ let s = null;
150
+ function h() {
151
+ s == null || s.destroy(), s = new L({
152
+ src: e.src,
153
+ rows: e.rows,
154
+ cols: e.cols,
155
+ fps: e.fps,
156
+ loop: e.loop,
157
+ width: e.width,
158
+ height: e.height,
159
+ autoPlay: e.autoPlay,
160
+ renderer: e.renderer,
161
+ onComplete: () => o("complete"),
162
+ onFrameChange: (a) => o("frameChange", a)
163
+ }), n.value && s.attach(n.value);
164
+ }
165
+ return T(h), P(() => {
166
+ s == null || s.destroy(), s = null;
167
+ }), k(
168
+ () => [
169
+ e.src,
170
+ e.rows,
171
+ e.cols,
172
+ e.fps,
173
+ e.loop,
174
+ e.width,
175
+ e.height,
176
+ e.autoPlay,
177
+ e.renderer
178
+ ],
179
+ h
180
+ ), t({
181
+ play: () => s == null ? void 0 : s.play(),
182
+ pause: () => s == null ? void 0 : s.pause(),
183
+ stop: () => s == null ? void 0 : s.stop(),
184
+ goToFrame: (a) => s == null ? void 0 : s.goToFrame(a),
185
+ getState: () => s == null ? void 0 : s.getState()
186
+ }), (a, l) => i.renderer === "canvas" ? (c(), u("canvas", {
187
+ key: 0,
188
+ ref_key: "targetRef",
189
+ ref: n,
190
+ class: m(e.class),
191
+ style: d({ width: `${i.width}px`, height: `${i.height}px` })
192
+ }, null, 6)) : (c(), u("div", {
193
+ key: 1,
194
+ ref_key: "targetRef",
195
+ ref: n,
196
+ class: m(e.class),
197
+ role: "img",
198
+ "aria-label": "Sprite animation",
199
+ style: d({ width: `${i.width}px`, height: `${i.height}px` })
200
+ }, null, 6));
201
+ }
202
+ });
203
+ export {
204
+ A as Sprite
205
+ };
package/package.json ADDED
@@ -0,0 +1,125 @@
1
+ {
2
+ "name": "mason-sprite",
3
+ "version": "0.1.0",
4
+ "description": "Lightweight sprite sheet animation for React, Vue, and Svelte",
5
+ "author": "mason <fe.hyunsu@gmail.com>",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "require": {
17
+ "types": "./dist/index.d.cts",
18
+ "default": "./dist/index.cjs"
19
+ }
20
+ },
21
+ "./react": {
22
+ "import": {
23
+ "types": "./dist/react/index.d.ts",
24
+ "default": "./dist/react/index.js"
25
+ },
26
+ "require": {
27
+ "types": "./dist/react/index.d.cts",
28
+ "default": "./dist/react/index.cjs"
29
+ }
30
+ },
31
+ "./vue": {
32
+ "import": {
33
+ "types": "./dist/vue/index.d.ts",
34
+ "default": "./dist/vue/index.js"
35
+ },
36
+ "require": {
37
+ "types": "./dist/vue/index.d.cts",
38
+ "default": "./dist/vue/index.cjs"
39
+ }
40
+ },
41
+ "./svelte": {
42
+ "types": "./dist/svelte/index.d.ts",
43
+ "svelte": "./dist/svelte/index.js",
44
+ "import": "./dist/svelte/index.js"
45
+ }
46
+ },
47
+ "files": [
48
+ "dist",
49
+ "README.md",
50
+ "LICENSE"
51
+ ],
52
+ "repository": {
53
+ "type": "git",
54
+ "url": "git+https://github.com/FE-HyunSu/mason-sprite.git"
55
+ },
56
+ "homepage": "https://github.com/FE-HyunSu/mason-sprite#readme",
57
+ "bugs": {
58
+ "url": "https://github.com/FE-HyunSu/mason-sprite/issues"
59
+ },
60
+ "scripts": {
61
+ "build": "pnpm run build:js && pnpm run build:vue && pnpm run build:svelte",
62
+ "build:js": "tsup",
63
+ "build:vue": "vite build --config vite.vue.config.ts",
64
+ "build:svelte": "svelte-package --input src/svelte --output dist/svelte",
65
+ "clean": "rm -rf dist .svelte-kit",
66
+ "dev": "tsup --watch",
67
+ "typecheck": "tsc --noEmit && svelte-check --tsconfig ./tsconfig.json",
68
+ "lint": "eslint .",
69
+ "format": "prettier --write .",
70
+ "format:check": "prettier --check .",
71
+ "prepublishOnly": "pnpm run build"
72
+ },
73
+ "peerDependencies": {
74
+ "react": ">=17",
75
+ "react-dom": ">=17",
76
+ "svelte": ">=4",
77
+ "vue": ">=3.3"
78
+ },
79
+ "peerDependenciesMeta": {
80
+ "react": { "optional": true },
81
+ "react-dom": { "optional": true },
82
+ "vue": { "optional": true },
83
+ "svelte": { "optional": true }
84
+ },
85
+ "devDependencies": {
86
+ "@eslint/js": "^9.28.0",
87
+ "@sveltejs/package": "^2.3.12",
88
+ "@sveltejs/vite-plugin-svelte": "^5.0.3",
89
+ "@types/node": "^22.15.29",
90
+ "@types/react": "^19.1.6",
91
+ "@types/react-dom": "^19.1.5",
92
+ "@vitejs/plugin-vue": "^5.2.4",
93
+ "eslint": "^9.28.0",
94
+ "eslint-config-prettier": "^10.1.5",
95
+ "eslint-plugin-svelte": "^3.9.0",
96
+ "globals": "^16.2.0",
97
+ "prettier": "^3.5.3",
98
+ "prettier-plugin-svelte": "^3.4.0",
99
+ "react": "^19.1.0",
100
+ "react-dom": "^19.1.0",
101
+ "svelte": "^5.33.14",
102
+ "svelte-check": "^4.2.1",
103
+ "tsup": "^8.5.0",
104
+ "typescript": "^5.8.3",
105
+ "typescript-eslint": "^8.33.1",
106
+ "vite": "^6.3.5",
107
+ "vite-plugin-dts": "^4.5.4",
108
+ "vue": "^3.5.16"
109
+ },
110
+ "keywords": [
111
+ "sprite",
112
+ "animation",
113
+ "spritesheet",
114
+ "react",
115
+ "vue",
116
+ "svelte",
117
+ "canvas",
118
+ "css"
119
+ ],
120
+ "license": "MIT",
121
+ "packageManager": "pnpm@9.15.9",
122
+ "engines": {
123
+ "node": ">=18"
124
+ }
125
+ }