@meganetaaan/mouse-follower
Version:
TypeScript library for creating animated sprites that smoothly follow mouse cursor or other targets using physics-based movement
137 lines (136 loc) • 4.01 kB
JavaScript
import { Animation } from "./animation.js";
import { Canvas } from "./canvas.js";
import { createSpriteContainer, createWrapper, processSpriteImage, } from "./sprite-utils.js";
/**
* New Sprite class with separated concerns
* Integrates Animation, Canvas, and DOM management
*/
export class Sprite {
#animation;
#canvas;
#wrapper;
#container;
#image;
#config;
#parent;
#isInitialized = false;
constructor(config, parent) {
this.#config = config;
this.#parent = parent;
// Initialize Animation controller
this.#animation = new Animation(config.animations, config.spriteWidth, config.spriteHeight, config.animationInterval);
// Initialize Canvas renderer
this.#canvas = new Canvas(config.spriteWidth, config.spriteHeight);
}
/**
* Initialize sprite - load image and set up DOM
*/
async initialize() {
if (this.#isInitialized) {
return;
}
// Load and process sprite image
this.#image = await processSpriteImage(this.#config.spriteUrl, this.#config.transparentColor);
// Create DOM structure
this.#wrapper = createWrapper();
this.#container = createSpriteContainer(this.#config.spriteWidth, this.#config.spriteHeight);
// Build DOM hierarchy
this.#container.appendChild(this.#canvas.getElement());
this.#wrapper.appendChild(this.#container);
// Attach to parent if provided
if (this.#parent) {
this.attach(this.#parent);
}
this.#isInitialized = true;
}
/**
* Attach sprite to DOM element
*/
attach(parent) {
if (!this.#wrapper) {
throw new Error("Sprite not initialized. Call initialize() first.");
}
// Remove from current parent if attached
if (this.#wrapper.parentElement) {
this.#wrapper.parentElement.removeChild(this.#wrapper);
}
// Attach to new parent
parent.appendChild(this.#wrapper);
this.#parent = parent;
}
/**
* Detach sprite from DOM
*/
detach() {
if (this.#wrapper?.parentElement) {
this.#wrapper.parentElement.removeChild(this.#wrapper);
}
}
/**
* Destroy sprite and clean up resources
*/
destroy() {
// Stop any running animation
this.#animation.pause();
// Remove from DOM
this.detach();
// Clear references
this.#wrapper = undefined;
this.#container = undefined;
this.#image = undefined;
this.#parent = undefined;
this.#isInitialized = false;
}
/**
* Render sprite at given position with direction (ISprite compatible)
*/
render(position, direction) {
if (!this.#isInitialized || !this.#image) {
return;
}
// Get current animation frame
const frame = this.#animation.getCurrentFrame();
// Render to canvas with default direction if not provided
const renderDirection = direction || "right";
this.#canvas.render(this.#image, frame, position.x, position.y, renderDirection);
}
/**
* Alternative render method accepting x, y coordinates
*/
renderAt(x, y, direction) {
this.render({ x, y }, direction);
}
/**
* Play animation by name
*/
playAnimation(name) {
if (!this.#isInitialized) {
return;
}
this.#animation.play(name);
}
/**
* Pause current animation
*/
pauseAnimation() {
this.#animation.pause();
}
/**
* Check if animation is currently playing
*/
isAnimating() {
return this.#animation.isPlaying;
}
/**
* Get current animation name
*/
getCurrentAnimation() {
return this.#animation.name;
}
/**
* Get current frame index
*/
getCurrentFrameIndex() {
return this.#animation.index;
}
}