Files
michaelschiemer/resources/js/modules/canvas-animations/CanvasManager.js
Michael Schiemer 55a330b223 Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
2025-08-11 20:13:26 +02:00

184 lines
5.1 KiB
JavaScript

// modules/canvas-animations/CanvasManager.js
import { useEvent } from '../../core/useEvent.js';
import { Logger } from '../../core/logger.js';
/**
* Canvas Manager - Handles canvas setup, resizing, and basic operations
*/
export class CanvasManager {
constructor(canvas, options = {}) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.options = {
responsive: true,
pixelRatio: window.devicePixelRatio || 1,
...options
};
this.animationId = null;
this.isAnimating = false;
this.init();
}
/**
* Initialize canvas manager
*/
init() {
this.setupCanvas();
if (this.options.responsive) {
this.setupResponsive();
}
Logger.info('[CanvasManager] Initialized', {
width: this.canvas.width,
height: this.canvas.height,
pixelRatio: this.options.pixelRatio
});
}
/**
* Setup initial canvas properties
*/
setupCanvas() {
this.resize();
// Set CSS to prevent blurriness on high-DPI displays
this.canvas.style.width = this.canvas.width + 'px';
this.canvas.style.height = this.canvas.height + 'px';
// Scale context for high-DPI displays
if (this.options.pixelRatio > 1) {
this.ctx.scale(this.options.pixelRatio, this.options.pixelRatio);
}
}
/**
* Setup responsive behavior
*/
setupResponsive() {
// Resize on window resize
useEvent(window, 'resize', () => {
this.resize();
}, 'canvas-manager');
// Optional: Resize on orientation change for mobile
useEvent(window, 'orientationchange', () => {
setTimeout(() => this.resize(), 100);
}, 'canvas-manager');
}
/**
* Resize canvas to match container or window
*/
resize() {
const container = this.canvas.parentElement;
const pixelRatio = this.options.pixelRatio;
// Get display size
let displayWidth, displayHeight;
if (container && getComputedStyle(container).position !== 'static') {
// Use container size if it has positioning
displayWidth = container.clientWidth;
displayHeight = container.clientHeight;
} else {
// Fallback to canvas CSS size or window size
displayWidth = this.canvas.clientWidth || window.innerWidth;
displayHeight = this.canvas.clientHeight || window.innerHeight;
}
// Set actual canvas size
this.canvas.width = Math.floor(displayWidth * pixelRatio);
this.canvas.height = Math.floor(displayHeight * pixelRatio);
// Set CSS size
this.canvas.style.width = displayWidth + 'px';
this.canvas.style.height = displayHeight + 'px';
// Re-scale context if needed
if (pixelRatio > 1) {
this.ctx.scale(pixelRatio, pixelRatio);
}
Logger.info('[CanvasManager] Resized', {
displayWidth,
displayHeight,
canvasWidth: this.canvas.width,
canvasHeight: this.canvas.height
});
}
/**
* Clear the entire canvas
*/
clear() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
/**
* Get canvas dimensions (display size, not pixel size)
*/
getSize() {
return {
width: this.canvas.clientWidth,
height: this.canvas.clientHeight,
pixelWidth: this.canvas.width,
pixelHeight: this.canvas.height
};
}
/**
* Get mouse position relative to canvas
*/
getMousePosition(event) {
const rect = this.canvas.getBoundingClientRect();
return {
x: (event.clientX - rect.left) * this.options.pixelRatio,
y: (event.clientY - rect.top) * this.options.pixelRatio
};
}
/**
* Start animation loop
*/
startAnimation(animationFunction) {
if (this.isAnimating) {
this.stopAnimation();
}
this.isAnimating = true;
const animate = (timestamp) => {
if (!this.isAnimating) return;
animationFunction(timestamp);
this.animationId = requestAnimationFrame(animate);
};
this.animationId = requestAnimationFrame(animate);
Logger.info('[CanvasManager] Animation started');
}
/**
* Stop animation loop
*/
stopAnimation() {
if (this.animationId) {
cancelAnimationFrame(this.animationId);
this.animationId = null;
}
this.isAnimating = false;
Logger.info('[CanvasManager] Animation stopped');
}
/**
* Cleanup canvas manager
*/
destroy() {
this.stopAnimation();
// Event cleanup is handled by useEvent system
Logger.info('[CanvasManager] Destroyed');
}
}