mason-sprite 0.1.1 → 0.1.2
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/LICENSE +21 -0
- package/README.md +145 -6
- package/dist/index.cjs +57 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -5
- package/dist/index.d.ts +12 -5
- package/dist/index.js +55 -10
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +68 -16
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +6 -4
- package/dist/react/index.d.ts +6 -4
- package/dist/react/index.js +68 -16
- package/dist/react/index.js.map +1 -1
- package/dist/svelte/Sprite.svelte +3 -2
- package/dist/svelte/Sprite.svelte.d.ts.map +1 -1
- package/dist/vue/Sprite.vue.d.ts +2 -2
- package/dist/vue/Sprite.vue.d.ts.map +1 -1
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.js +89 -69
- package/package.json +37 -26
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/Sprite.tsx","../../src/core/constants.ts","../../src/core/utils.ts","../../src/core/canvas-renderer.ts","../../src/core/css-renderer.ts","../../src/core/sprite-animator.ts","../../src/react/useSprite.ts"],"sourcesContent":["import type { SpriteAnimationOptions } from '../core/types.js';\nimport { forwardRef, useImperativeHandle } from 'react';\nimport { useSprite } from './useSprite.js';\n\nexport interface SpriteProps extends SpriteAnimationOptions {\n className?: string;\n style?: React.CSSProperties;\n}\n\nexport interface SpriteHandle {\n play: () => void;\n pause: () => void;\n stop: () => void;\n goToFrame: (frame: number) => void;\n}\n\nexport const Sprite = forwardRef<SpriteHandle, SpriteProps>(function Sprite(\n { className, style, ...options },\n ref,\n) {\n const { ref: targetRef, play, pause, stop, goToFrame } = useSprite(options);\n\n useImperativeHandle(ref, () => ({ play, pause, stop, goToFrame }), [\n play,\n pause,\n stop,\n goToFrame,\n ]);\n\n if (options.renderer === 'canvas') {\n return (\n <canvas\n ref={targetRef as React.RefObject<HTMLCanvasElement>}\n className={className}\n style={style}\n />\n );\n }\n\n return (\n <div\n ref={targetRef as React.RefObject<HTMLDivElement>}\n className={className}\n style={style}\n role=\"img\"\n aria-label=\"Sprite animation\"\n />\n );\n});\n","import type { RendererMode } from './types.js';\n\nexport const SPRITE_ANIMATION_DEFAULTS = {\n fps: 12,\n loop: true,\n width: 128,\n height: 128,\n autoPlay: true,\n renderer: 'css' as RendererMode,\n} as const;\n","import type { FramePosition } from './types.js';\n\nexport function getTotalFrames(rows: number, cols: number): number {\n return rows * cols;\n}\n\nexport function getFramePosition(frameIndex: number, cols: number): FramePosition {\n return {\n row: Math.floor(frameIndex / cols),\n col: frameIndex % cols,\n };\n}\n\nexport function getBackgroundPositionPercent(\n frameIndex: number,\n rows: number,\n cols: number,\n): { x: number; y: number } {\n const { row, col } = getFramePosition(frameIndex, cols);\n const x = cols <= 1 ? 0 : (col / (cols - 1)) * 100;\n const y = rows <= 1 ? 0 : (row / (rows - 1)) * 100;\n return { x, y };\n}\n","import { getFramePosition } from './utils.js';\n\nexport function drawCanvasFrame(\n canvas: HTMLCanvasElement,\n image: HTMLImageElement,\n frameIndex: number,\n rows: number,\n cols: number,\n width: number,\n height: number,\n): void {\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const frameWidth = image.naturalWidth / cols;\n const frameHeight = image.naturalHeight / rows;\n const { row, col } = getFramePosition(frameIndex, cols);\n\n canvas.width = width;\n canvas.height = height;\n\n ctx.clearRect(0, 0, width, height);\n ctx.drawImage(\n image,\n col * frameWidth,\n row * frameHeight,\n frameWidth,\n frameHeight,\n 0,\n 0,\n width,\n height,\n );\n}\n","import { getBackgroundPositionPercent } from './utils.js';\n\nexport interface CssRendererTarget {\n style: CSSStyleDeclaration;\n}\n\nexport function applyCssFrame(\n target: CssRendererTarget,\n src: string,\n frameIndex: number,\n rows: number,\n cols: number,\n width: number,\n height: number,\n): void {\n const { x, y } = getBackgroundPositionPercent(frameIndex, rows, cols);\n\n target.style.backgroundImage = `url(\"${src}\")`;\n target.style.backgroundRepeat = 'no-repeat';\n target.style.backgroundSize = `${cols * 100}% ${rows * 100}%`;\n target.style.backgroundPosition = `${x}% ${y}%`;\n target.style.width = `${width}px`;\n target.style.height = `${height}px`;\n target.style.display = 'inline-block';\n}\n\nexport function resetCssRenderer(target: CssRendererTarget): void {\n target.style.backgroundImage = '';\n}\n","import { SPRITE_ANIMATION_DEFAULTS } from './constants.js';\nimport { drawCanvasFrame } from './canvas-renderer.js';\nimport { applyCssFrame, resetCssRenderer } from './css-renderer.js';\nimport type { SpriteAnimationOptions, SpriteAnimationState } from './types.js';\nimport { getTotalFrames } from './utils.js';\n\ntype StateListener = (state: SpriteAnimationState) => void;\n\ntype ResolvedSpriteAnimationOptions = Required<\n Pick<SpriteAnimationOptions, 'src' | 'rows' | 'cols'>\n> &\n Required<Pick<SpriteAnimationOptions, 'fps' | 'loop' | 'width' | 'height' | 'autoPlay' | 'renderer'>> &\n Pick<SpriteAnimationOptions, 'onComplete' | 'onFrameChange'>;\n\nexport class SpriteAnimator {\n private options: ResolvedSpriteAnimationOptions;\n private currentFrame = 0;\n private isPlaying = false;\n private isLoaded = false;\n private rafId: number | null = null;\n private lastTimestamp = 0;\n private accumulatedTime = 0;\n private image: HTMLImageElement | null = null;\n private target: HTMLElement | HTMLCanvasElement | null = null;\n private listeners = new Set<StateListener>();\n private destroyed = false;\n\n constructor(options: SpriteAnimationOptions) {\n this.options = {\n ...SPRITE_ANIMATION_DEFAULTS,\n ...options,\n };\n this.loadImage();\n }\n\n attach(target: HTMLElement | HTMLCanvasElement): void {\n this.target = target;\n if (this.isLoaded) {\n this.render();\n }\n if (this.options.autoPlay) {\n this.play();\n }\n }\n\n play(): void {\n if (this.destroyed || this.isPlaying) return;\n this.isPlaying = true;\n this.lastTimestamp = 0;\n this.accumulatedTime = 0;\n this.rafId = requestAnimationFrame(this.tick);\n this.notify();\n }\n\n pause(): void {\n if (!this.isPlaying) return;\n this.isPlaying = false;\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n this.notify();\n }\n\n stop(): void {\n this.pause();\n this.currentFrame = 0;\n this.render();\n this.notify();\n }\n\n goToFrame(frame: number): void {\n const total = this.getTotalFrames();\n this.currentFrame = Math.max(0, Math.min(frame, total - 1));\n this.render();\n this.options.onFrameChange?.(this.currentFrame);\n this.notify();\n }\n\n getState(): SpriteAnimationState {\n return {\n currentFrame: this.currentFrame,\n totalFrames: this.getTotalFrames(),\n isPlaying: this.isPlaying,\n isLoaded: this.isLoaded,\n };\n }\n\n subscribe(listener: StateListener): () => void {\n this.listeners.add(listener);\n listener(this.getState());\n return () => this.listeners.delete(listener);\n }\n\n updateOptions(partial: Partial<SpriteAnimationOptions>): void {\n const prevSrc = this.options.src;\n const prevFps = this.options.fps;\n this.options = { ...this.options, ...partial };\n\n if (partial.src !== undefined && partial.src !== prevSrc) {\n this.loadImage();\n } else if (this.isLoaded) {\n this.render();\n }\n\n if (partial.fps !== undefined && partial.fps !== prevFps) {\n this.accumulatedTime = 0;\n }\n }\n\n destroy(): void {\n this.destroyed = true;\n this.pause();\n this.listeners.clear();\n if (this.target && this.options.renderer === 'css') {\n resetCssRenderer(this.target);\n }\n this.target = null;\n this.image = null;\n }\n\n private getTotalFrames(): number {\n return getTotalFrames(this.options.rows, this.options.cols);\n }\n\n private loadImage(): void {\n this.isLoaded = false;\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => {\n if (this.destroyed) return;\n this.image = img;\n this.isLoaded = true;\n\n if (!this.options.width || !this.options.height) {\n const frameWidth = img.naturalWidth / this.options.cols;\n const frameHeight = img.naturalHeight / this.options.rows;\n this.options.width = frameWidth;\n this.options.height = frameHeight;\n }\n\n this.render();\n this.notify();\n\n if (this.options.autoPlay && this.target) {\n this.play();\n }\n };\n img.onerror = () => {\n console.error(`[SpriteAnimator] Failed to load image: ${this.options.src}`);\n };\n img.src = this.options.src;\n }\n\n private tick = (timestamp: number): void => {\n if (!this.isPlaying || this.destroyed) return;\n\n if (this.lastTimestamp === 0) {\n this.lastTimestamp = timestamp;\n }\n\n const delta = timestamp - this.lastTimestamp;\n this.lastTimestamp = timestamp;\n this.accumulatedTime += delta;\n\n const frameDuration = 1000 / this.options.fps;\n while (this.accumulatedTime >= frameDuration) {\n this.accumulatedTime -= frameDuration;\n this.advanceFrame();\n }\n\n this.rafId = requestAnimationFrame(this.tick);\n };\n\n private advanceFrame(): void {\n const total = this.getTotalFrames();\n const next = this.currentFrame + 1;\n\n if (next >= total) {\n if (this.options.loop) {\n this.currentFrame = 0;\n } else {\n this.currentFrame = total - 1;\n this.pause();\n this.options.onComplete?.();\n }\n } else {\n this.currentFrame = next;\n }\n\n this.render();\n this.options.onFrameChange?.(this.currentFrame);\n this.notify();\n }\n\n private render(): void {\n if (!this.target || !this.isLoaded) return;\n\n const { src, rows, cols, width, height, renderer } = this.options;\n\n if (renderer === 'canvas' && this.target instanceof HTMLCanvasElement && this.image) {\n drawCanvasFrame(this.target, this.image, this.currentFrame, rows, cols, width, height);\n } else if (renderer === 'css' && this.target instanceof HTMLElement) {\n applyCssFrame(this.target, src, this.currentFrame, rows, cols, width, height);\n }\n }\n\n private notify(): void {\n const state = this.getState();\n this.listeners.forEach((listener) => listener(state));\n }\n}\n","import {\n SpriteAnimator,\n type SpriteAnimationOptions,\n type SpriteAnimationState,\n} from '../core/index.js';\nimport { useEffect, useRef, useState } from 'react';\n\nexport interface UseSpriteOptions extends SpriteAnimationOptions {\n /** Skip auto-attach; useful when controlling the target manually */\n enabled?: boolean;\n}\n\nexport interface UseSpriteReturn {\n ref: React.RefObject<HTMLElement | HTMLCanvasElement | null>;\n state: SpriteAnimationState;\n play: () => void;\n pause: () => void;\n stop: () => void;\n goToFrame: (frame: number) => void;\n}\n\nexport function useSprite(options: UseSpriteOptions): UseSpriteReturn {\n const ref = useRef<HTMLElement | HTMLCanvasElement | null>(null);\n const animatorRef = useRef<SpriteAnimator | null>(null);\n const [state, setState] = useState<SpriteAnimationState>({\n currentFrame: 0,\n totalFrames: options.rows * options.cols,\n isPlaying: false,\n isLoaded: false,\n });\n\n const { enabled = true, ...animatorOptions } = options;\n\n useEffect(() => {\n if (!enabled) return;\n\n const animator = new SpriteAnimator(animatorOptions);\n animatorRef.current = animator;\n\n const unsubscribe = animator.subscribe(setState);\n\n queueMicrotask(() => {\n if (ref.current) {\n animator.attach(ref.current);\n }\n });\n\n return () => {\n unsubscribe();\n animator.destroy();\n animatorRef.current = null;\n };\n }, [enabled]);\n\n useEffect(() => {\n animatorRef.current?.updateOptions(animatorOptions);\n }, [\n animatorOptions.src,\n animatorOptions.rows,\n animatorOptions.cols,\n animatorOptions.fps,\n animatorOptions.loop,\n animatorOptions.width,\n animatorOptions.height,\n animatorOptions.autoPlay,\n animatorOptions.renderer,\n ]);\n\n return {\n ref,\n state,\n play: () => animatorRef.current?.play(),\n pause: () => animatorRef.current?.pause(),\n stop: () => animatorRef.current?.stop(),\n goToFrame: (frame) => animatorRef.current?.goToFrame(frame),\n };\n}\n"],"mappings":"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","names":["Sprite"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/constants.ts","../../src/core/utils.ts","../../src/core/canvas-renderer.ts","../../src/core/css-renderer.ts","../../src/core/sprite-animator.ts","../../src/react/Sprite.tsx","../../src/react/useSprite.ts"],"sourcesContent":["import type { RendererMode } from './types.js';\n\nexport const SPRITE_ANIMATION_DEFAULTS = {\n fps: 12,\n loop: true,\n width: 128,\n height: 128,\n autoPlay: true,\n renderer: 'css' as RendererMode,\n} as const;\n","import type { FramePosition, SpriteSize } from './types.js';\n\n/** Converts a SpriteSize to a CSS length string. Numbers become `px`; strings pass through. */\nexport function toCssLength(size: SpriteSize): string {\n return typeof size === 'number' ? `${size}px` : size;\n}\n\nexport function getTotalFrames(rows: number, cols: number): number {\n return rows * cols;\n}\n\nexport function getFramePosition(frameIndex: number, cols: number): FramePosition {\n return {\n row: Math.floor(frameIndex / cols),\n col: frameIndex % cols,\n };\n}\n\nexport function getBackgroundPositionPercent(\n frameIndex: number,\n rows: number,\n cols: number,\n): { x: number; y: number } {\n const { row, col } = getFramePosition(frameIndex, cols);\n const x = cols <= 1 ? 0 : (col / (cols - 1)) * 100;\n const y = rows <= 1 ? 0 : (row / (rows - 1)) * 100;\n return { x, y };\n}\n","import { getFramePosition } from './utils.js';\n\nexport function drawCanvasFrame(\n canvas: HTMLCanvasElement,\n image: HTMLImageElement,\n frameIndex: number,\n rows: number,\n cols: number,\n): void {\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const displayWidth = canvas.clientWidth;\n const displayHeight = canvas.clientHeight;\n if (displayWidth === 0 || displayHeight === 0) return;\n\n const dpr = window.devicePixelRatio || 1;\n const pixelWidth = Math.round(displayWidth * dpr);\n const pixelHeight = Math.round(displayHeight * dpr);\n\n if (canvas.width !== pixelWidth || canvas.height !== pixelHeight) {\n canvas.width = pixelWidth;\n canvas.height = pixelHeight;\n }\n\n const frameWidth = image.naturalWidth / cols;\n const frameHeight = image.naturalHeight / rows;\n const { row, col } = getFramePosition(frameIndex, cols);\n\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, displayWidth, displayHeight);\n ctx.drawImage(\n image,\n col * frameWidth,\n row * frameHeight,\n frameWidth,\n frameHeight,\n 0,\n 0,\n displayWidth,\n displayHeight,\n );\n}\n","import type { SpriteSize } from './types.js';\nimport { getBackgroundPositionPercent, toCssLength } from './utils.js';\n\nexport interface CssRendererTarget {\n style: CSSStyleDeclaration;\n}\n\nexport function applyCssFrame(\n target: CssRendererTarget,\n src: string,\n frameIndex: number,\n rows: number,\n cols: number,\n width: SpriteSize,\n height: SpriteSize,\n): void {\n const { x, y } = getBackgroundPositionPercent(frameIndex, rows, cols);\n\n target.style.backgroundImage = `url(\"${src}\")`;\n target.style.backgroundRepeat = 'no-repeat';\n target.style.backgroundSize = `${cols * 100}% ${rows * 100}%`;\n target.style.backgroundPosition = `${x}% ${y}%`;\n target.style.width = toCssLength(width);\n target.style.height = toCssLength(height);\n target.style.display = 'inline-block';\n}\n\nexport function resetCssRenderer(target: CssRendererTarget): void {\n target.style.backgroundImage = '';\n}\n","import { SPRITE_ANIMATION_DEFAULTS } from './constants.js';\nimport { drawCanvasFrame } from './canvas-renderer.js';\nimport { applyCssFrame, resetCssRenderer } from './css-renderer.js';\nimport type { SpriteAnimationOptions, SpriteAnimationState } from './types.js';\nimport { getTotalFrames, toCssLength } from './utils.js';\n\ntype StateListener = (state: SpriteAnimationState) => void;\n\ntype ResolvedSpriteAnimationOptions = Required<\n Pick<SpriteAnimationOptions, 'src' | 'rows' | 'cols'>\n> &\n Required<Pick<SpriteAnimationOptions, 'fps' | 'loop' | 'width' | 'height' | 'autoPlay' | 'renderer'>> &\n Pick<SpriteAnimationOptions, 'onComplete' | 'onFrameChange'>;\n\nexport class SpriteAnimator {\n private options: ResolvedSpriteAnimationOptions;\n private currentFrame = 0;\n private isPlaying = false;\n private isLoaded = false;\n private rafId: number | null = null;\n private lastTimestamp = 0;\n private accumulatedTime = 0;\n private image: HTMLImageElement | null = null;\n private target: HTMLElement | HTMLCanvasElement | null = null;\n private listeners = new Set<StateListener>();\n private destroyed = false;\n private resizeObserver: ResizeObserver | null = null;\n\n constructor(options: SpriteAnimationOptions) {\n this.options = {\n ...SPRITE_ANIMATION_DEFAULTS,\n ...options,\n };\n this.loadImage();\n }\n\n attach(target: HTMLElement | HTMLCanvasElement): void {\n this.target = target;\n this.applyCanvasDisplaySize();\n this.setupResizeObserver();\n if (this.isLoaded) {\n this.render();\n }\n if (this.options.autoPlay) {\n this.play();\n }\n }\n\n play(): void {\n if (this.destroyed || this.isPlaying) return;\n this.isPlaying = true;\n this.lastTimestamp = 0;\n this.accumulatedTime = 0;\n this.rafId = requestAnimationFrame(this.tick);\n this.notify();\n }\n\n pause(): void {\n if (!this.isPlaying) return;\n this.isPlaying = false;\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n this.notify();\n }\n\n stop(): void {\n this.pause();\n this.currentFrame = 0;\n this.render();\n this.notify();\n }\n\n goToFrame(frame: number): void {\n const total = this.getTotalFrames();\n this.currentFrame = Math.max(0, Math.min(frame, total - 1));\n this.render();\n this.options.onFrameChange?.(this.currentFrame);\n this.notify();\n }\n\n getState(): SpriteAnimationState {\n return {\n currentFrame: this.currentFrame,\n totalFrames: this.getTotalFrames(),\n isPlaying: this.isPlaying,\n isLoaded: this.isLoaded,\n };\n }\n\n subscribe(listener: StateListener): () => void {\n this.listeners.add(listener);\n listener(this.getState());\n return () => this.listeners.delete(listener);\n }\n\n updateOptions(partial: Partial<SpriteAnimationOptions>): void {\n const prevSrc = this.options.src;\n const prevFps = this.options.fps;\n const prevRenderer = this.options.renderer;\n this.options = { ...this.options, ...partial };\n\n if (partial.src !== undefined && partial.src !== prevSrc) {\n this.loadImage();\n } else if (this.isLoaded) {\n this.render();\n }\n\n if (partial.fps !== undefined && partial.fps !== prevFps) {\n this.accumulatedTime = 0;\n }\n\n if (partial.width !== undefined || partial.height !== undefined) {\n this.applyCanvasDisplaySize();\n }\n\n if (partial.renderer !== undefined && partial.renderer !== prevRenderer) {\n this.setupResizeObserver();\n }\n }\n\n destroy(): void {\n this.destroyed = true;\n this.pause();\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.listeners.clear();\n if (this.target && this.options.renderer === 'css') {\n resetCssRenderer(this.target);\n }\n this.target = null;\n this.image = null;\n }\n\n private getTotalFrames(): number {\n return getTotalFrames(this.options.rows, this.options.cols);\n }\n\n private loadImage(): void {\n this.isLoaded = false;\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => {\n if (this.destroyed) return;\n this.image = img;\n this.isLoaded = true;\n\n if (!this.options.width || !this.options.height) {\n const frameWidth = img.naturalWidth / this.options.cols;\n const frameHeight = img.naturalHeight / this.options.rows;\n this.options.width = frameWidth;\n this.options.height = frameHeight;\n }\n\n this.render();\n this.notify();\n\n if (this.options.autoPlay && this.target) {\n this.play();\n }\n };\n img.onerror = () => {\n console.error(`[SpriteAnimator] Failed to load image: ${this.options.src}`);\n };\n img.src = this.options.src;\n }\n\n private tick = (timestamp: number): void => {\n if (!this.isPlaying || this.destroyed) return;\n\n if (this.lastTimestamp === 0) {\n this.lastTimestamp = timestamp;\n }\n\n const delta = timestamp - this.lastTimestamp;\n this.lastTimestamp = timestamp;\n this.accumulatedTime += delta;\n\n const frameDuration = 1000 / this.options.fps;\n while (this.accumulatedTime >= frameDuration) {\n this.accumulatedTime -= frameDuration;\n this.advanceFrame();\n }\n\n this.rafId = requestAnimationFrame(this.tick);\n };\n\n private advanceFrame(): void {\n const total = this.getTotalFrames();\n const next = this.currentFrame + 1;\n\n if (next >= total) {\n if (this.options.loop) {\n this.currentFrame = 0;\n } else {\n this.currentFrame = total - 1;\n this.pause();\n this.options.onComplete?.();\n }\n } else {\n this.currentFrame = next;\n }\n\n this.render();\n this.options.onFrameChange?.(this.currentFrame);\n this.notify();\n }\n\n private render(): void {\n if (!this.target || !this.isLoaded) return;\n\n const { src, rows, cols, width, height, renderer } = this.options;\n\n if (renderer === 'canvas' && this.target instanceof HTMLCanvasElement && this.image) {\n drawCanvasFrame(this.target, this.image, this.currentFrame, rows, cols);\n } else if (renderer === 'css' && this.target instanceof HTMLElement) {\n applyCssFrame(this.target, src, this.currentFrame, rows, cols, width, height);\n }\n }\n\n private notify(): void {\n const state = this.getState();\n this.listeners.forEach((listener) => listener(state));\n }\n\n private applyCanvasDisplaySize(): void {\n if (this.options.renderer !== 'canvas' || !(this.target instanceof HTMLCanvasElement)) {\n return;\n }\n this.target.style.width = toCssLength(this.options.width);\n this.target.style.height = toCssLength(this.options.height);\n }\n\n private setupResizeObserver(): void {\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n\n if (\n this.options.renderer !== 'canvas' ||\n !(this.target instanceof HTMLCanvasElement) ||\n typeof ResizeObserver === 'undefined'\n ) {\n return;\n }\n\n this.resizeObserver = new ResizeObserver(() => {\n if (this.isLoaded) {\n this.render();\n }\n });\n this.resizeObserver.observe(this.target);\n }\n}\n","import type { SpriteAnimationOptions } from '../core/types.js';\nimport { SPRITE_ANIMATION_DEFAULTS, toCssLength } from '../core/index.js';\nimport { forwardRef, useImperativeHandle } from 'react';\nimport { useSprite } from './useSprite.js';\n\nexport interface SpriteProps extends SpriteAnimationOptions {\n className?: string;\n style?: React.CSSProperties;\n}\n\nexport interface SpriteHandle {\n play: () => void;\n pause: () => void;\n stop: () => void;\n goToFrame: (frame: number) => void;\n}\n\nexport const Sprite = forwardRef<SpriteHandle, SpriteProps>(function Sprite(\n { className, style, width, height, ...options },\n ref,\n) {\n const { ref: targetRef, play, pause, stop, goToFrame } = useSprite({\n width,\n height,\n ...options,\n });\n\n useImperativeHandle(ref, () => ({ play, pause, stop, goToFrame }), [\n play,\n pause,\n stop,\n goToFrame,\n ]);\n\n const sizeStyle = {\n width: toCssLength(width ?? SPRITE_ANIMATION_DEFAULTS.width),\n height: toCssLength(height ?? SPRITE_ANIMATION_DEFAULTS.height),\n };\n\n if (options.renderer === 'canvas') {\n return (\n <canvas\n ref={targetRef as React.RefObject<HTMLCanvasElement>}\n className={className}\n style={{ ...sizeStyle, ...style }}\n />\n );\n }\n\n return (\n <div\n ref={targetRef as React.RefObject<HTMLDivElement>}\n className={className}\n style={{ ...sizeStyle, ...style }}\n role=\"img\"\n aria-label=\"Sprite animation\"\n />\n );\n});\n","import {\n SpriteAnimator,\n type SpriteAnimationOptions,\n type SpriteAnimationState,\n} from '../core/index.js';\nimport { useEffect, useRef, useState } from 'react';\n\nexport interface UseSpriteOptions extends SpriteAnimationOptions {\n /** Skip auto-attach; useful when controlling the target manually */\n enabled?: boolean;\n}\n\nexport interface UseSpriteReturn {\n ref: React.RefObject<HTMLElement | HTMLCanvasElement | null>;\n state: SpriteAnimationState;\n play: () => void;\n pause: () => void;\n stop: () => void;\n goToFrame: (frame: number) => void;\n}\n\nexport function useSprite(options: UseSpriteOptions): UseSpriteReturn {\n const ref = useRef<HTMLElement | HTMLCanvasElement | null>(null);\n const animatorRef = useRef<SpriteAnimator | null>(null);\n const [state, setState] = useState<SpriteAnimationState>({\n currentFrame: 0,\n totalFrames: options.rows * options.cols,\n isPlaying: false,\n isLoaded: false,\n });\n\n const { enabled = true, ...animatorOptions } = options;\n\n useEffect(() => {\n if (!enabled) return;\n\n const animator = new SpriteAnimator(animatorOptions);\n animatorRef.current = animator;\n\n const unsubscribe = animator.subscribe(setState);\n\n queueMicrotask(() => {\n if (ref.current) {\n animator.attach(ref.current);\n }\n });\n\n return () => {\n unsubscribe();\n animator.destroy();\n animatorRef.current = null;\n };\n }, [enabled]);\n\n useEffect(() => {\n animatorRef.current?.updateOptions(animatorOptions);\n }, [\n animatorOptions.src,\n animatorOptions.rows,\n animatorOptions.cols,\n animatorOptions.fps,\n animatorOptions.loop,\n animatorOptions.width,\n animatorOptions.height,\n animatorOptions.autoPlay,\n animatorOptions.renderer,\n ]);\n\n return {\n ref,\n state,\n play: () => animatorRef.current?.play(),\n pause: () => animatorRef.current?.pause(),\n stop: () => animatorRef.current?.stop(),\n goToFrame: (frame) => animatorRef.current?.goToFrame(frame),\n };\n}\n"],"mappings":";AAEO,IAAM,4BAA4B;AAAA,EACvC,KAAK;AAAA,EACL,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,UAAU;AACZ;;;ACNO,SAAS,YAAY,MAA0B;AACpD,SAAO,OAAO,SAAS,WAAW,GAAG,IAAI,OAAO;AAClD;AAEO,SAAS,eAAe,MAAc,MAAsB;AACjE,SAAO,OAAO;AAChB;AAEO,SAAS,iBAAiB,YAAoB,MAA6B;AAChF,SAAO;AAAA,IACL,KAAK,KAAK,MAAM,aAAa,IAAI;AAAA,IACjC,KAAK,aAAa;AAAA,EACpB;AACF;AAEO,SAAS,6BACd,YACA,MACA,MAC0B;AAC1B,QAAM,EAAE,KAAK,IAAI,IAAI,iBAAiB,YAAY,IAAI;AACtD,QAAM,IAAI,QAAQ,IAAI,IAAK,OAAO,OAAO,KAAM;AAC/C,QAAM,IAAI,QAAQ,IAAI,IAAK,OAAO,OAAO,KAAM;AAC/C,SAAO,EAAE,GAAG,EAAE;AAChB;;;ACzBO,SAAS,gBACd,QACA,OACA,YACA,MACA,MACM;AACN,QAAM,MAAM,OAAO,WAAW,IAAI;AAClC,MAAI,CAAC,IAAK;AAEV,QAAM,eAAe,OAAO;AAC5B,QAAM,gBAAgB,OAAO;AAC7B,MAAI,iBAAiB,KAAK,kBAAkB,EAAG;AAE/C,QAAM,MAAM,OAAO,oBAAoB;AACvC,QAAM,aAAa,KAAK,MAAM,eAAe,GAAG;AAChD,QAAM,cAAc,KAAK,MAAM,gBAAgB,GAAG;AAElD,MAAI,OAAO,UAAU,cAAc,OAAO,WAAW,aAAa;AAChE,WAAO,QAAQ;AACf,WAAO,SAAS;AAAA,EAClB;AAEA,QAAM,aAAa,MAAM,eAAe;AACxC,QAAM,cAAc,MAAM,gBAAgB;AAC1C,QAAM,EAAE,KAAK,IAAI,IAAI,iBAAiB,YAAY,IAAI;AAEtD,MAAI,aAAa,KAAK,GAAG,GAAG,KAAK,GAAG,CAAC;AACrC,MAAI,UAAU,GAAG,GAAG,cAAc,aAAa;AAC/C,MAAI;AAAA,IACF;AAAA,IACA,MAAM;AAAA,IACN,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACnCO,SAAS,cACd,QACA,KACA,YACA,MACA,MACA,OACA,QACM;AACN,QAAM,EAAE,GAAG,EAAE,IAAI,6BAA6B,YAAY,MAAM,IAAI;AAEpE,SAAO,MAAM,kBAAkB,QAAQ,GAAG;AAC1C,SAAO,MAAM,mBAAmB;AAChC,SAAO,MAAM,iBAAiB,GAAG,OAAO,GAAG,KAAK,OAAO,GAAG;AAC1D,SAAO,MAAM,qBAAqB,GAAG,CAAC,KAAK,CAAC;AAC5C,SAAO,MAAM,QAAQ,YAAY,KAAK;AACtC,SAAO,MAAM,SAAS,YAAY,MAAM;AACxC,SAAO,MAAM,UAAU;AACzB;AAEO,SAAS,iBAAiB,QAAiC;AAChE,SAAO,MAAM,kBAAkB;AACjC;;;ACfO,IAAM,iBAAN,MAAqB;AAAA,EAc1B,YAAY,SAAiC;AAZ7C,SAAQ,eAAe;AACvB,SAAQ,YAAY;AACpB,SAAQ,WAAW;AACnB,SAAQ,QAAuB;AAC/B,SAAQ,gBAAgB;AACxB,SAAQ,kBAAkB;AAC1B,SAAQ,QAAiC;AACzC,SAAQ,SAAiD;AACzD,SAAQ,YAAY,oBAAI,IAAmB;AAC3C,SAAQ,YAAY;AACpB,SAAQ,iBAAwC;AA8IhD,SAAQ,OAAO,CAAC,cAA4B;AAC1C,UAAI,CAAC,KAAK,aAAa,KAAK,UAAW;AAEvC,UAAI,KAAK,kBAAkB,GAAG;AAC5B,aAAK,gBAAgB;AAAA,MACvB;AAEA,YAAM,QAAQ,YAAY,KAAK;AAC/B,WAAK,gBAAgB;AACrB,WAAK,mBAAmB;AAExB,YAAM,gBAAgB,MAAO,KAAK,QAAQ;AAC1C,aAAO,KAAK,mBAAmB,eAAe;AAC5C,aAAK,mBAAmB;AACxB,aAAK,aAAa;AAAA,MACpB;AAEA,WAAK,QAAQ,sBAAsB,KAAK,IAAI;AAAA,IAC9C;AA7JE,SAAK,UAAU;AAAA,MACb,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AACA,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,OAAO,QAA+C;AACpD,SAAK,SAAS;AACd,SAAK,uBAAuB;AAC5B,SAAK,oBAAoB;AACzB,QAAI,KAAK,UAAU;AACjB,WAAK,OAAO;AAAA,IACd;AACA,QAAI,KAAK,QAAQ,UAAU;AACzB,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AAAA,EAEA,OAAa;AACX,QAAI,KAAK,aAAa,KAAK,UAAW;AACtC,SAAK,YAAY;AACjB,SAAK,gBAAgB;AACrB,SAAK,kBAAkB;AACvB,SAAK,QAAQ,sBAAsB,KAAK,IAAI;AAC5C,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,QAAc;AACZ,QAAI,CAAC,KAAK,UAAW;AACrB,SAAK,YAAY;AACjB,QAAI,KAAK,UAAU,MAAM;AACvB,2BAAqB,KAAK,KAAK;AAC/B,WAAK,QAAQ;AAAA,IACf;AACA,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,OAAa;AACX,SAAK,MAAM;AACX,SAAK,eAAe;AACpB,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,UAAU,OAAqB;AAC7B,UAAM,QAAQ,KAAK,eAAe;AAClC,SAAK,eAAe,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,QAAQ,CAAC,CAAC;AAC1D,SAAK,OAAO;AACZ,SAAK,QAAQ,gBAAgB,KAAK,YAAY;AAC9C,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,WAAiC;AAC/B,WAAO;AAAA,MACL,cAAc,KAAK;AAAA,MACnB,aAAa,KAAK,eAAe;AAAA,MACjC,WAAW,KAAK;AAAA,MAChB,UAAU,KAAK;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,UAAU,UAAqC;AAC7C,SAAK,UAAU,IAAI,QAAQ;AAC3B,aAAS,KAAK,SAAS,CAAC;AACxB,WAAO,MAAM,KAAK,UAAU,OAAO,QAAQ;AAAA,EAC7C;AAAA,EAEA,cAAc,SAAgD;AAC5D,UAAM,UAAU,KAAK,QAAQ;AAC7B,UAAM,UAAU,KAAK,QAAQ;AAC7B,UAAM,eAAe,KAAK,QAAQ;AAClC,SAAK,UAAU,EAAE,GAAG,KAAK,SAAS,GAAG,QAAQ;AAE7C,QAAI,QAAQ,QAAQ,UAAa,QAAQ,QAAQ,SAAS;AACxD,WAAK,UAAU;AAAA,IACjB,WAAW,KAAK,UAAU;AACxB,WAAK,OAAO;AAAA,IACd;AAEA,QAAI,QAAQ,QAAQ,UAAa,QAAQ,QAAQ,SAAS;AACxD,WAAK,kBAAkB;AAAA,IACzB;AAEA,QAAI,QAAQ,UAAU,UAAa,QAAQ,WAAW,QAAW;AAC/D,WAAK,uBAAuB;AAAA,IAC9B;AAEA,QAAI,QAAQ,aAAa,UAAa,QAAQ,aAAa,cAAc;AACvE,WAAK,oBAAoB;AAAA,IAC3B;AAAA,EACF;AAAA,EAEA,UAAgB;AACd,SAAK,YAAY;AACjB,SAAK,MAAM;AACX,SAAK,gBAAgB,WAAW;AAChC,SAAK,iBAAiB;AACtB,SAAK,UAAU,MAAM;AACrB,QAAI,KAAK,UAAU,KAAK,QAAQ,aAAa,OAAO;AAClD,uBAAiB,KAAK,MAAM;AAAA,IAC9B;AACA,SAAK,SAAS;AACd,SAAK,QAAQ;AAAA,EACf;AAAA,EAEQ,iBAAyB;AAC/B,WAAO,eAAe,KAAK,QAAQ,MAAM,KAAK,QAAQ,IAAI;AAAA,EAC5D;AAAA,EAEQ,YAAkB;AACxB,SAAK,WAAW;AAChB,UAAM,MAAM,IAAI,MAAM;AACtB,QAAI,cAAc;AAClB,QAAI,SAAS,MAAM;AACjB,UAAI,KAAK,UAAW;AACpB,WAAK,QAAQ;AACb,WAAK,WAAW;AAEhB,UAAI,CAAC,KAAK,QAAQ,SAAS,CAAC,KAAK,QAAQ,QAAQ;AAC/C,cAAM,aAAa,IAAI,eAAe,KAAK,QAAQ;AACnD,cAAM,cAAc,IAAI,gBAAgB,KAAK,QAAQ;AACrD,aAAK,QAAQ,QAAQ;AACrB,aAAK,QAAQ,SAAS;AAAA,MACxB;AAEA,WAAK,OAAO;AACZ,WAAK,OAAO;AAEZ,UAAI,KAAK,QAAQ,YAAY,KAAK,QAAQ;AACxC,aAAK,KAAK;AAAA,MACZ;AAAA,IACF;AACA,QAAI,UAAU,MAAM;AAClB,cAAQ,MAAM,0CAA0C,KAAK,QAAQ,GAAG,EAAE;AAAA,IAC5E;AACA,QAAI,MAAM,KAAK,QAAQ;AAAA,EACzB;AAAA,EAsBQ,eAAqB;AAC3B,UAAM,QAAQ,KAAK,eAAe;AAClC,UAAM,OAAO,KAAK,eAAe;AAEjC,QAAI,QAAQ,OAAO;AACjB,UAAI,KAAK,QAAQ,MAAM;AACrB,aAAK,eAAe;AAAA,MACtB,OAAO;AACL,aAAK,eAAe,QAAQ;AAC5B,aAAK,MAAM;AACX,aAAK,QAAQ,aAAa;AAAA,MAC5B;AAAA,IACF,OAAO;AACL,WAAK,eAAe;AAAA,IACtB;AAEA,SAAK,OAAO;AACZ,SAAK,QAAQ,gBAAgB,KAAK,YAAY;AAC9C,SAAK,OAAO;AAAA,EACd;AAAA,EAEQ,SAAe;AACrB,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,SAAU;AAEpC,UAAM,EAAE,KAAK,MAAM,MAAM,OAAO,QAAQ,SAAS,IAAI,KAAK;AAE1D,QAAI,aAAa,YAAY,KAAK,kBAAkB,qBAAqB,KAAK,OAAO;AACnF,sBAAgB,KAAK,QAAQ,KAAK,OAAO,KAAK,cAAc,MAAM,IAAI;AAAA,IACxE,WAAW,aAAa,SAAS,KAAK,kBAAkB,aAAa;AACnE,oBAAc,KAAK,QAAQ,KAAK,KAAK,cAAc,MAAM,MAAM,OAAO,MAAM;AAAA,IAC9E;AAAA,EACF;AAAA,EAEQ,SAAe;AACrB,UAAM,QAAQ,KAAK,SAAS;AAC5B,SAAK,UAAU,QAAQ,CAAC,aAAa,SAAS,KAAK,CAAC;AAAA,EACtD;AAAA,EAEQ,yBAA+B;AACrC,QAAI,KAAK,QAAQ,aAAa,YAAY,EAAE,KAAK,kBAAkB,oBAAoB;AACrF;AAAA,IACF;AACA,SAAK,OAAO,MAAM,QAAQ,YAAY,KAAK,QAAQ,KAAK;AACxD,SAAK,OAAO,MAAM,SAAS,YAAY,KAAK,QAAQ,MAAM;AAAA,EAC5D;AAAA,EAEQ,sBAA4B;AAClC,SAAK,gBAAgB,WAAW;AAChC,SAAK,iBAAiB;AAEtB,QACE,KAAK,QAAQ,aAAa,YAC1B,EAAE,KAAK,kBAAkB,sBACzB,OAAO,mBAAmB,aAC1B;AACA;AAAA,IACF;AAEA,SAAK,iBAAiB,IAAI,eAAe,MAAM;AAC7C,UAAI,KAAK,UAAU;AACjB,aAAK,OAAO;AAAA,MACd;AAAA,IACF,CAAC;AACD,SAAK,eAAe,QAAQ,KAAK,MAAM;AAAA,EACzC;AACF;;;AC3PA,SAAS,YAAY,2BAA2B;;;ACGhD,SAAS,WAAW,QAAQ,gBAAgB;AAgBrC,SAAS,UAAU,SAA4C;AACpE,QAAM,MAAM,OAA+C,IAAI;AAC/D,QAAM,cAAc,OAA8B,IAAI;AACtD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA+B;AAAA,IACvD,cAAc;AAAA,IACd,aAAa,QAAQ,OAAO,QAAQ;AAAA,IACpC,WAAW;AAAA,IACX,UAAU;AAAA,EACZ,CAAC;AAED,QAAM,EAAE,UAAU,MAAM,GAAG,gBAAgB,IAAI;AAE/C,YAAU,MAAM;AACd,QAAI,CAAC,QAAS;AAEd,UAAM,WAAW,IAAI,eAAe,eAAe;AACnD,gBAAY,UAAU;AAEtB,UAAM,cAAc,SAAS,UAAU,QAAQ;AAE/C,mBAAe,MAAM;AACnB,UAAI,IAAI,SAAS;AACf,iBAAS,OAAO,IAAI,OAAO;AAAA,MAC7B;AAAA,IACF,CAAC;AAED,WAAO,MAAM;AACX,kBAAY;AACZ,eAAS,QAAQ;AACjB,kBAAY,UAAU;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAEZ,YAAU,MAAM;AACd,gBAAY,SAAS,cAAc,eAAe;AAAA,EACpD,GAAG;AAAA,IACD,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,EAClB,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,MAAM,MAAM,YAAY,SAAS,KAAK;AAAA,IACtC,OAAO,MAAM,YAAY,SAAS,MAAM;AAAA,IACxC,MAAM,MAAM,YAAY,SAAS,KAAK;AAAA,IACtC,WAAW,CAAC,UAAU,YAAY,SAAS,UAAU,KAAK;AAAA,EAC5D;AACF;;;ADnCM;AAxBC,IAAM,SAAS,WAAsC,SAASA,QACnE,EAAE,WAAW,OAAO,OAAO,QAAQ,GAAG,QAAQ,GAC9C,KACA;AACA,QAAM,EAAE,KAAK,WAAW,MAAM,OAAO,MAAM,UAAU,IAAI,UAAU;AAAA,IACjE;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,CAAC;AAED,sBAAoB,KAAK,OAAO,EAAE,MAAM,OAAO,MAAM,UAAU,IAAI;AAAA,IACjE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,YAAY;AAAA,IAChB,OAAO,YAAY,SAAS,0BAA0B,KAAK;AAAA,IAC3D,QAAQ,YAAY,UAAU,0BAA0B,MAAM;AAAA,EAChE;AAEA,MAAI,QAAQ,aAAa,UAAU;AACjC,WACE;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL;AAAA,QACA,OAAO,EAAE,GAAG,WAAW,GAAG,MAAM;AAAA;AAAA,IAClC;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,OAAO,EAAE,GAAG,WAAW,GAAG,MAAM;AAAA,MAChC,MAAK;AAAA,MACL,cAAW;AAAA;AAAA,EACb;AAEJ,CAAC;","names":["Sprite"]}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
SPRITE_ANIMATION_DEFAULTS,
|
|
4
4
|
SpriteAnimator,
|
|
5
|
+
toCssLength,
|
|
5
6
|
type SpriteAnimationOptions,
|
|
6
7
|
} from 'mason-sprite';
|
|
7
8
|
|
|
@@ -76,13 +77,13 @@
|
|
|
76
77
|
</script>
|
|
77
78
|
|
|
78
79
|
{#if renderer === 'canvas'}
|
|
79
|
-
<canvas bind:this={target} class={className} style="width: {width}
|
|
80
|
+
<canvas bind:this={target} class={className} style="width: {toCssLength(width)}; height: {toCssLength(height)};"></canvas>
|
|
80
81
|
{:else}
|
|
81
82
|
<div
|
|
82
83
|
bind:this={target}
|
|
83
84
|
class={className}
|
|
84
85
|
role="img"
|
|
85
86
|
aria-label="Sprite animation"
|
|
86
|
-
style="width: {width}
|
|
87
|
+
style="width: {toCssLength(width)}; height: {toCssLength(height)};"
|
|
87
88
|
></div>
|
|
88
89
|
{/if}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sprite.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/Sprite.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"Sprite.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/Sprite.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAIH,KAAK,sBAAsB,EAC5B,MAAM,cAAc,CAAC;AAGtB,UAAU,KAAM,SAAQ,sBAAsB;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+EH,QAAA,MAAM,MAAM;;;;uBAjBiB,MAAM;;MAiBiB,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
|
package/dist/vue/Sprite.vue.d.ts
CHANGED
|
@@ -17,8 +17,8 @@ declare const _default: import('vue').DefineComponent<__VLS_Props, {
|
|
|
17
17
|
}>, {
|
|
18
18
|
fps: number;
|
|
19
19
|
loop: boolean;
|
|
20
|
-
width:
|
|
21
|
-
height:
|
|
20
|
+
width: import('../core/types.js').SpriteSize;
|
|
21
|
+
height: import('../core/types.js').SpriteSize;
|
|
22
22
|
autoPlay: boolean;
|
|
23
23
|
renderer: import('../core/types.js').RendererMode;
|
|
24
24
|
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sprite.vue.d.ts","sourceRoot":"","sources":["../../src/vue/Sprite.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Sprite.vue.d.ts","sourceRoot":"","sources":["../../src/vue/Sprite.vue"],"names":[],"mappings":"AAgGA,OAAO,EAA0D,KAAK,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAGvH,KAAK,WAAW,GAAG,sBAAsB,GAAG;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;;;;;uBAqEe,MAAM;;;;;;;;;;;;;;;;;;AAsE3B,wBAUG"}
|
package/dist/vue/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("vue"),f={fps:12,loop:!0,width:128,height:128,autoPlay:!0,renderer:"css"};function d(s){return typeof s=="number"?`${s}px`:s}function v(s,t){return s*t}function g(s,t){return{row:Math.floor(s/t),col:s%t}}function w(s,t,r){const{row:e,col:n}=g(s,r),a=r<=1?0:n/(r-1)*100,o=t<=1?0:e/(t-1)*100;return{x:a,y:o}}function T(s,t,r,e,n){const a=s.getContext("2d");if(!a)return;const o=s.clientWidth,i=s.clientHeight;if(o===0||i===0)return;const l=window.devicePixelRatio||1,c=Math.round(o*l),u=Math.round(i*l);(s.width!==c||s.height!==u)&&(s.width=c,s.height=u);const p=t.naturalWidth/n,m=t.naturalHeight/e,{row:y,col:F}=g(r,n);a.setTransform(l,0,0,l,0,0),a.clearRect(0,0,o,i),a.drawImage(t,F*p,y*m,p,m,0,0,o,i)}function b(s,t,r,e,n,a,o){const{x:i,y:l}=w(r,e,n);s.style.backgroundImage=`url("${t}")`,s.style.backgroundRepeat="no-repeat",s.style.backgroundSize=`${n*100}% ${e*100}%`,s.style.backgroundPosition=`${i}% ${l}%`,s.style.width=d(a),s.style.height=d(o),s.style.display="inline-block"}function C(s){s.style.backgroundImage=""}class S{constructor(t){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=new Set,this.destroyed=!1,this.resizeObserver=null,this.tick=r=>{if(!this.isPlaying||this.destroyed)return;this.lastTimestamp===0&&(this.lastTimestamp=r);const e=r-this.lastTimestamp;this.lastTimestamp=r,this.accumulatedTime+=e;const n=1e3/this.options.fps;for(;this.accumulatedTime>=n;)this.accumulatedTime-=n,this.advanceFrame();this.rafId=requestAnimationFrame(this.tick)},this.options={...f,...t},this.loadImage()}attach(t){this.target=t,this.applyCanvasDisplaySize(),this.setupResizeObserver(),this.isLoaded&&this.render(),this.options.autoPlay&&this.play()}play(){this.destroyed||this.isPlaying||(this.isPlaying=!0,this.lastTimestamp=0,this.accumulatedTime=0,this.rafId=requestAnimationFrame(this.tick),this.notify())}pause(){this.isPlaying&&(this.isPlaying=!1,this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.notify())}stop(){this.pause(),this.currentFrame=0,this.render(),this.notify()}goToFrame(t){var e,n;const r=this.getTotalFrames();this.currentFrame=Math.max(0,Math.min(t,r-1)),this.render(),(n=(e=this.options).onFrameChange)==null||n.call(e,this.currentFrame),this.notify()}getState(){return{currentFrame:this.currentFrame,totalFrames:this.getTotalFrames(),isPlaying:this.isPlaying,isLoaded:this.isLoaded}}subscribe(t){return this.listeners.add(t),t(this.getState()),()=>this.listeners.delete(t)}updateOptions(t){const r=this.options.src,e=this.options.fps,n=this.options.renderer;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),(t.width!==void 0||t.height!==void 0)&&this.applyCanvasDisplaySize(),t.renderer!==void 0&&t.renderer!==n&&this.setupResizeObserver()}destroy(){var t;this.destroyed=!0,this.pause(),(t=this.resizeObserver)==null||t.disconnect(),this.resizeObserver=null,this.listeners.clear(),this.target&&this.options.renderer==="css"&&C(this.target),this.target=null,this.image=null}getTotalFrames(){return v(this.options.rows,this.options.cols)}loadImage(){this.isLoaded=!1;const t=new Image;t.crossOrigin="anonymous",t.onload=()=>{if(!this.destroyed){if(this.image=t,this.isLoaded=!0,!this.options.width||!this.options.height){const r=t.naturalWidth/this.options.cols,e=t.naturalHeight/this.options.rows;this.options.width=r,this.options.height=e}this.render(),this.notify(),this.options.autoPlay&&this.target&&this.play()}},t.onerror=()=>{console.error(`[SpriteAnimator] Failed to load image: ${this.options.src}`)},t.src=this.options.src}advanceFrame(){var e,n,a,o;const t=this.getTotalFrames(),r=this.currentFrame+1;r>=t?this.options.loop?this.currentFrame=0:(this.currentFrame=t-1,this.pause(),(n=(e=this.options).onComplete)==null||n.call(e)):this.currentFrame=r,this.render(),(o=(a=this.options).onFrameChange)==null||o.call(a,this.currentFrame),this.notify()}render(){if(!this.target||!this.isLoaded)return;const{src:t,rows:r,cols:e,width:n,height:a,renderer:o}=this.options;o==="canvas"&&this.target instanceof HTMLCanvasElement&&this.image?T(this.target,this.image,this.currentFrame,r,e):o==="css"&&this.target instanceof HTMLElement&&b(this.target,t,this.currentFrame,r,e,n,a)}notify(){const t=this.getState();this.listeners.forEach(r=>r(t))}applyCanvasDisplaySize(){this.options.renderer!=="canvas"||!(this.target instanceof HTMLCanvasElement)||(this.target.style.width=d(this.options.width),this.target.style.height=d(this.options.height))}setupResizeObserver(){var t;(t=this.resizeObserver)==null||t.disconnect(),this.resizeObserver=null,!(this.options.renderer!=="canvas"||!(this.target instanceof HTMLCanvasElement)||typeof ResizeObserver>"u")&&(this.resizeObserver=new ResizeObserver(()=>{this.isLoaded&&this.render()}),this.resizeObserver.observe(this.target))}}const P=h.defineComponent({__name:"Sprite",props:h.mergeDefaults({src:{},rows:{},cols:{},fps:{},loop:{type:Boolean},width:{},height:{},autoPlay:{type:Boolean},renderer:{},onComplete:{type:Function},onFrameChange:{type:Function},class:{}},f),emits:["complete","frameChange"],setup(s,{expose:t,emit:r}){const e=s,n=h.computed(()=>({width:d(e.width),height:d(e.height)})),a=r,o=h.ref(null);let i=null;function l(){i==null||i.destroy(),i=new S({src:e.src,rows:e.rows,cols:e.cols,fps:e.fps,loop:e.loop,width:e.width,height:e.height,autoPlay:e.autoPlay,renderer:e.renderer,onComplete:()=>a("complete"),onFrameChange:c=>a("frameChange",c)}),o.value&&i.attach(o.value)}return h.onMounted(l),h.onBeforeUnmount(()=>{i==null||i.destroy(),i=null}),h.watch(()=>[e.src,e.rows,e.cols,e.fps,e.loop,e.width,e.height,e.autoPlay,e.renderer],l),t({play:()=>i==null?void 0:i.play(),pause:()=>i==null?void 0:i.pause(),stop:()=>i==null?void 0:i.stop(),goToFrame:c=>i==null?void 0:i.goToFrame(c),getState:()=>i==null?void 0:i.getState()}),(c,u)=>s.renderer==="canvas"?(h.openBlock(),h.createElementBlock("canvas",{key:0,ref_key:"targetRef",ref:o,class:h.normalizeClass(e.class),style:h.normalizeStyle(n.value)},null,6)):(h.openBlock(),h.createElementBlock("div",{key:1,ref_key:"targetRef",ref:o,class:h.normalizeClass(e.class),role:"img","aria-label":"Sprite animation",style:h.normalizeStyle(n.value)},null,6))}});exports.Sprite=P;
|
package/dist/vue/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { defineComponent as
|
|
2
|
-
const
|
|
1
|
+
import { defineComponent as b, computed as C, ref as P, onMounted as S, onBeforeUnmount as z, watch as k, openBlock as m, createElementBlock as f, normalizeStyle as g, normalizeClass as y, mergeDefaults as I } from "vue";
|
|
2
|
+
const F = {
|
|
3
3
|
fps: 12,
|
|
4
4
|
loop: !0,
|
|
5
5
|
width: 128,
|
|
@@ -7,60 +7,67 @@ const p = {
|
|
|
7
7
|
autoPlay: !0,
|
|
8
8
|
renderer: "css"
|
|
9
9
|
};
|
|
10
|
-
function
|
|
11
|
-
return
|
|
10
|
+
function c(s) {
|
|
11
|
+
return typeof s == "number" ? `${s}px` : s;
|
|
12
12
|
}
|
|
13
|
-
function
|
|
13
|
+
function L(s, t) {
|
|
14
|
+
return s * t;
|
|
15
|
+
}
|
|
16
|
+
function v(s, t) {
|
|
14
17
|
return {
|
|
15
|
-
row: Math.floor(
|
|
16
|
-
col:
|
|
18
|
+
row: Math.floor(s / t),
|
|
19
|
+
col: s % t
|
|
17
20
|
};
|
|
18
21
|
}
|
|
19
|
-
function
|
|
20
|
-
const { row: e, col:
|
|
21
|
-
return { x:
|
|
22
|
+
function O(s, t, r) {
|
|
23
|
+
const { row: e, col: n } = v(s, r), a = r <= 1 ? 0 : n / (r - 1) * 100, o = t <= 1 ? 0 : e / (t - 1) * 100;
|
|
24
|
+
return { x: a, y: o };
|
|
22
25
|
}
|
|
23
|
-
function
|
|
24
|
-
const
|
|
25
|
-
if (!
|
|
26
|
-
const
|
|
27
|
-
|
|
26
|
+
function R(s, t, r, e, n) {
|
|
27
|
+
const a = s.getContext("2d");
|
|
28
|
+
if (!a) return;
|
|
29
|
+
const o = s.clientWidth, i = s.clientHeight;
|
|
30
|
+
if (o === 0 || i === 0) return;
|
|
31
|
+
const h = window.devicePixelRatio || 1, l = Math.round(o * h), d = Math.round(i * h);
|
|
32
|
+
(s.width !== l || s.height !== d) && (s.width = l, s.height = d);
|
|
33
|
+
const u = t.naturalWidth / n, p = t.naturalHeight / e, { row: w, col: T } = v(r, n);
|
|
34
|
+
a.setTransform(h, 0, 0, h, 0, 0), a.clearRect(0, 0, o, i), a.drawImage(
|
|
28
35
|
t,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
T * u,
|
|
37
|
+
w * p,
|
|
38
|
+
u,
|
|
39
|
+
p,
|
|
33
40
|
0,
|
|
34
41
|
0,
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
o,
|
|
43
|
+
i
|
|
37
44
|
);
|
|
38
45
|
}
|
|
39
|
-
function
|
|
40
|
-
const { x:
|
|
41
|
-
|
|
46
|
+
function x(s, t, r, e, n, a, o) {
|
|
47
|
+
const { x: i, y: h } = O(r, e, n);
|
|
48
|
+
s.style.backgroundImage = `url("${t}")`, s.style.backgroundRepeat = "no-repeat", s.style.backgroundSize = `${n * 100}% ${e * 100}%`, s.style.backgroundPosition = `${i}% ${h}%`, s.style.width = c(a), s.style.height = c(o), s.style.display = "inline-block";
|
|
42
49
|
}
|
|
43
|
-
function
|
|
44
|
-
|
|
50
|
+
function H(s) {
|
|
51
|
+
s.style.backgroundImage = "";
|
|
45
52
|
}
|
|
46
|
-
class
|
|
53
|
+
class M {
|
|
47
54
|
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) => {
|
|
55
|
+
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.resizeObserver = null, this.tick = (r) => {
|
|
49
56
|
if (!this.isPlaying || this.destroyed) return;
|
|
50
57
|
this.lastTimestamp === 0 && (this.lastTimestamp = r);
|
|
51
58
|
const e = r - this.lastTimestamp;
|
|
52
59
|
this.lastTimestamp = r, this.accumulatedTime += e;
|
|
53
|
-
const
|
|
54
|
-
for (; this.accumulatedTime >=
|
|
55
|
-
this.accumulatedTime -=
|
|
60
|
+
const n = 1e3 / this.options.fps;
|
|
61
|
+
for (; this.accumulatedTime >= n; )
|
|
62
|
+
this.accumulatedTime -= n, this.advanceFrame();
|
|
56
63
|
this.rafId = requestAnimationFrame(this.tick);
|
|
57
64
|
}, this.options = {
|
|
58
|
-
...
|
|
65
|
+
...F,
|
|
59
66
|
...t
|
|
60
67
|
}, this.loadImage();
|
|
61
68
|
}
|
|
62
69
|
attach(t) {
|
|
63
|
-
this.target = t, this.isLoaded && this.render(), this.options.autoPlay && this.play();
|
|
70
|
+
this.target = t, this.applyCanvasDisplaySize(), this.setupResizeObserver(), this.isLoaded && this.render(), this.options.autoPlay && this.play();
|
|
64
71
|
}
|
|
65
72
|
play() {
|
|
66
73
|
this.destroyed || this.isPlaying || (this.isPlaying = !0, this.lastTimestamp = 0, this.accumulatedTime = 0, this.rafId = requestAnimationFrame(this.tick), this.notify());
|
|
@@ -72,9 +79,9 @@ class L {
|
|
|
72
79
|
this.pause(), this.currentFrame = 0, this.render(), this.notify();
|
|
73
80
|
}
|
|
74
81
|
goToFrame(t) {
|
|
75
|
-
var e,
|
|
82
|
+
var e, n;
|
|
76
83
|
const r = this.getTotalFrames();
|
|
77
|
-
this.currentFrame = Math.max(0, Math.min(t, r - 1)), this.render(), (
|
|
84
|
+
this.currentFrame = Math.max(0, Math.min(t, r - 1)), this.render(), (n = (e = this.options).onFrameChange) == null || n.call(e, this.currentFrame), this.notify();
|
|
78
85
|
}
|
|
79
86
|
getState() {
|
|
80
87
|
return {
|
|
@@ -88,14 +95,15 @@ class L {
|
|
|
88
95
|
return this.listeners.add(t), t(this.getState()), () => this.listeners.delete(t);
|
|
89
96
|
}
|
|
90
97
|
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);
|
|
98
|
+
const r = this.options.src, e = this.options.fps, n = this.options.renderer;
|
|
99
|
+
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), (t.width !== void 0 || t.height !== void 0) && this.applyCanvasDisplaySize(), t.renderer !== void 0 && t.renderer !== n && this.setupResizeObserver();
|
|
93
100
|
}
|
|
94
101
|
destroy() {
|
|
95
|
-
|
|
102
|
+
var t;
|
|
103
|
+
this.destroyed = !0, this.pause(), (t = this.resizeObserver) == null || t.disconnect(), this.resizeObserver = null, this.listeners.clear(), this.target && this.options.renderer === "css" && H(this.target), this.target = null, this.image = null;
|
|
96
104
|
}
|
|
97
105
|
getTotalFrames() {
|
|
98
|
-
return
|
|
106
|
+
return L(this.options.rows, this.options.cols);
|
|
99
107
|
}
|
|
100
108
|
loadImage() {
|
|
101
109
|
this.isLoaded = !1;
|
|
@@ -113,23 +121,32 @@ class L {
|
|
|
113
121
|
}, t.src = this.options.src;
|
|
114
122
|
}
|
|
115
123
|
advanceFrame() {
|
|
116
|
-
var e,
|
|
124
|
+
var e, n, a, o;
|
|
117
125
|
const t = this.getTotalFrames(), r = this.currentFrame + 1;
|
|
118
|
-
r >= t ? this.options.loop ? this.currentFrame = 0 : (this.currentFrame = t - 1, this.pause(), (
|
|
126
|
+
r >= t ? this.options.loop ? this.currentFrame = 0 : (this.currentFrame = t - 1, this.pause(), (n = (e = this.options).onComplete) == null || n.call(e)) : this.currentFrame = r, this.render(), (o = (a = this.options).onFrameChange) == null || o.call(a, this.currentFrame), this.notify();
|
|
119
127
|
}
|
|
120
128
|
render() {
|
|
121
129
|
if (!this.target || !this.isLoaded) return;
|
|
122
|
-
const { src: t, rows: r, cols: e, width:
|
|
123
|
-
|
|
130
|
+
const { src: t, rows: r, cols: e, width: n, height: a, renderer: o } = this.options;
|
|
131
|
+
o === "canvas" && this.target instanceof HTMLCanvasElement && this.image ? R(this.target, this.image, this.currentFrame, r, e) : o === "css" && this.target instanceof HTMLElement && x(this.target, t, this.currentFrame, r, e, n, a);
|
|
124
132
|
}
|
|
125
133
|
notify() {
|
|
126
134
|
const t = this.getState();
|
|
127
135
|
this.listeners.forEach((r) => r(t));
|
|
128
136
|
}
|
|
137
|
+
applyCanvasDisplaySize() {
|
|
138
|
+
this.options.renderer !== "canvas" || !(this.target instanceof HTMLCanvasElement) || (this.target.style.width = c(this.options.width), this.target.style.height = c(this.options.height));
|
|
139
|
+
}
|
|
140
|
+
setupResizeObserver() {
|
|
141
|
+
var t;
|
|
142
|
+
(t = this.resizeObserver) == null || t.disconnect(), this.resizeObserver = null, !(this.options.renderer !== "canvas" || !(this.target instanceof HTMLCanvasElement) || typeof ResizeObserver > "u") && (this.resizeObserver = new ResizeObserver(() => {
|
|
143
|
+
this.isLoaded && this.render();
|
|
144
|
+
}), this.resizeObserver.observe(this.target));
|
|
145
|
+
}
|
|
129
146
|
}
|
|
130
|
-
const
|
|
147
|
+
const E = /* @__PURE__ */ b({
|
|
131
148
|
__name: "Sprite",
|
|
132
|
-
props: /* @__PURE__ */
|
|
149
|
+
props: /* @__PURE__ */ I({
|
|
133
150
|
src: {},
|
|
134
151
|
rows: {},
|
|
135
152
|
cols: {},
|
|
@@ -142,13 +159,16 @@ const A = /* @__PURE__ */ F({
|
|
|
142
159
|
onComplete: { type: Function },
|
|
143
160
|
onFrameChange: { type: Function },
|
|
144
161
|
class: {}
|
|
145
|
-
},
|
|
162
|
+
}, F),
|
|
146
163
|
emits: ["complete", "frameChange"],
|
|
147
|
-
setup(
|
|
148
|
-
const e =
|
|
149
|
-
|
|
164
|
+
setup(s, { expose: t, emit: r }) {
|
|
165
|
+
const e = s, n = C(() => ({
|
|
166
|
+
width: c(e.width),
|
|
167
|
+
height: c(e.height)
|
|
168
|
+
})), a = r, o = P(null);
|
|
169
|
+
let i = null;
|
|
150
170
|
function h() {
|
|
151
|
-
|
|
171
|
+
i == null || i.destroy(), i = new M({
|
|
152
172
|
src: e.src,
|
|
153
173
|
rows: e.rows,
|
|
154
174
|
cols: e.cols,
|
|
@@ -158,12 +178,12 @@ const A = /* @__PURE__ */ F({
|
|
|
158
178
|
height: e.height,
|
|
159
179
|
autoPlay: e.autoPlay,
|
|
160
180
|
renderer: e.renderer,
|
|
161
|
-
onComplete: () =>
|
|
162
|
-
onFrameChange: (
|
|
163
|
-
}),
|
|
181
|
+
onComplete: () => a("complete"),
|
|
182
|
+
onFrameChange: (l) => a("frameChange", l)
|
|
183
|
+
}), o.value && i.attach(o.value);
|
|
164
184
|
}
|
|
165
|
-
return
|
|
166
|
-
|
|
185
|
+
return S(h), z(() => {
|
|
186
|
+
i == null || i.destroy(), i = null;
|
|
167
187
|
}), k(
|
|
168
188
|
() => [
|
|
169
189
|
e.src,
|
|
@@ -178,28 +198,28 @@ const A = /* @__PURE__ */ F({
|
|
|
178
198
|
],
|
|
179
199
|
h
|
|
180
200
|
), t({
|
|
181
|
-
play: () =>
|
|
182
|
-
pause: () =>
|
|
183
|
-
stop: () =>
|
|
184
|
-
goToFrame: (
|
|
185
|
-
getState: () =>
|
|
186
|
-
}), (
|
|
201
|
+
play: () => i == null ? void 0 : i.play(),
|
|
202
|
+
pause: () => i == null ? void 0 : i.pause(),
|
|
203
|
+
stop: () => i == null ? void 0 : i.stop(),
|
|
204
|
+
goToFrame: (l) => i == null ? void 0 : i.goToFrame(l),
|
|
205
|
+
getState: () => i == null ? void 0 : i.getState()
|
|
206
|
+
}), (l, d) => s.renderer === "canvas" ? (m(), f("canvas", {
|
|
187
207
|
key: 0,
|
|
188
208
|
ref_key: "targetRef",
|
|
189
|
-
ref:
|
|
190
|
-
class:
|
|
191
|
-
style:
|
|
192
|
-
}, null, 6)) : (
|
|
209
|
+
ref: o,
|
|
210
|
+
class: y(e.class),
|
|
211
|
+
style: g(n.value)
|
|
212
|
+
}, null, 6)) : (m(), f("div", {
|
|
193
213
|
key: 1,
|
|
194
214
|
ref_key: "targetRef",
|
|
195
|
-
ref:
|
|
196
|
-
class:
|
|
215
|
+
ref: o,
|
|
216
|
+
class: y(e.class),
|
|
197
217
|
role: "img",
|
|
198
218
|
"aria-label": "Sprite animation",
|
|
199
|
-
style:
|
|
219
|
+
style: g(n.value)
|
|
200
220
|
}, null, 6));
|
|
201
221
|
}
|
|
202
222
|
});
|
|
203
223
|
export {
|
|
204
|
-
|
|
224
|
+
E as Sprite
|
|
205
225
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mason-sprite",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Lightweight sprite sheet animation for React, Vue, and Svelte",
|
|
5
|
+
"author": "mason <fe.hyunsu@gmail.com>",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"main": "./dist/index.cjs",
|
|
7
8
|
"module": "./dist/index.js",
|
|
@@ -45,16 +46,29 @@
|
|
|
45
46
|
},
|
|
46
47
|
"files": [
|
|
47
48
|
"dist",
|
|
48
|
-
"README.md"
|
|
49
|
+
"README.md",
|
|
50
|
+
"LICENSE"
|
|
49
51
|
],
|
|
50
52
|
"repository": {
|
|
51
53
|
"type": "git",
|
|
52
|
-
"url": "git+https://github.com/FE-HyunSu/sprite
|
|
53
|
-
"directory": "packages/mason-sprite"
|
|
54
|
+
"url": "git+https://github.com/FE-HyunSu/mason-sprite.git"
|
|
54
55
|
},
|
|
55
|
-
"homepage": "https://
|
|
56
|
+
"homepage": "https://mason-sprite.com",
|
|
56
57
|
"bugs": {
|
|
57
|
-
"url": "https://github.com/FE-HyunSu/sprite
|
|
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"
|
|
58
72
|
},
|
|
59
73
|
"peerDependencies": {
|
|
60
74
|
"react": ">=17",
|
|
@@ -63,31 +77,32 @@
|
|
|
63
77
|
"vue": ">=3.3"
|
|
64
78
|
},
|
|
65
79
|
"peerDependenciesMeta": {
|
|
66
|
-
"react": {
|
|
67
|
-
|
|
68
|
-
},
|
|
69
|
-
"
|
|
70
|
-
"optional": true
|
|
71
|
-
},
|
|
72
|
-
"vue": {
|
|
73
|
-
"optional": true
|
|
74
|
-
},
|
|
75
|
-
"svelte": {
|
|
76
|
-
"optional": true
|
|
77
|
-
}
|
|
80
|
+
"react": { "optional": true },
|
|
81
|
+
"react-dom": { "optional": true },
|
|
82
|
+
"vue": { "optional": true },
|
|
83
|
+
"svelte": { "optional": true }
|
|
78
84
|
},
|
|
79
85
|
"devDependencies": {
|
|
86
|
+
"@eslint/js": "^9.28.0",
|
|
80
87
|
"@sveltejs/package": "^2.3.12",
|
|
81
88
|
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
|
89
|
+
"@types/node": "^22.15.29",
|
|
82
90
|
"@types/react": "^19.1.6",
|
|
83
91
|
"@types/react-dom": "^19.1.5",
|
|
84
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",
|
|
85
99
|
"react": "^19.1.0",
|
|
86
100
|
"react-dom": "^19.1.0",
|
|
87
101
|
"svelte": "^5.33.14",
|
|
88
102
|
"svelte-check": "^4.2.1",
|
|
89
103
|
"tsup": "^8.5.0",
|
|
90
104
|
"typescript": "^5.8.3",
|
|
105
|
+
"typescript-eslint": "^8.33.1",
|
|
91
106
|
"vite": "^6.3.5",
|
|
92
107
|
"vite-plugin-dts": "^4.5.4",
|
|
93
108
|
"vue": "^3.5.16"
|
|
@@ -103,12 +118,8 @@
|
|
|
103
118
|
"css"
|
|
104
119
|
],
|
|
105
120
|
"license": "MIT",
|
|
106
|
-
"
|
|
107
|
-
|
|
108
|
-
"
|
|
109
|
-
"build:vue": "vite build --config vite.vue.config.ts",
|
|
110
|
-
"build:svelte": "svelte-package --input src/svelte --output dist/svelte",
|
|
111
|
-
"typecheck": "tsc --noEmit && svelte-check --tsconfig ./tsconfig.json",
|
|
112
|
-
"dev": "tsup --watch"
|
|
121
|
+
"packageManager": "pnpm@9.15.9",
|
|
122
|
+
"engines": {
|
|
123
|
+
"node": ">=18"
|
|
113
124
|
}
|
|
114
|
-
}
|
|
125
|
+
}
|