export const zoom = function(superClass) { return class extends superClass { static get properties() { return { scale: { type: Number } }; } constructor() { super(); this.scale = 1; this._handleWheel = this._handleWheel.bind(this); this.MIN_SCALE = 0.1; this.MAX_SCALE = 1.0; this.SCALE_STEP = 0.1; } firstUpdated() { super.firstUpdated(); this.canvas = this.shadowRoot.querySelector('.canvas'); this.addEventListener('wheel', this._handleWheel, { passive: false }); } _handleWheel(e) { e.preventDefault(); if (e.deltaY > 0) { this.zoomOut(); } else if (e.deltaY < 0) { this.zoomIn(); } } /** * Zooms out the canvas by one step * @returns {boolean} True if zoom was applied, false if already at minimum */ zoomOut() { const newScale = Math.max(this.MIN_SCALE, this.scale - this.SCALE_STEP); if (newScale !== this.scale) { this._applyZoom(newScale); return true; } return false; } /** * Zooms in the canvas by one step * @returns {boolean} True if zoom was applied, false if already at maximum */ zoomIn() { const newScale = Math.min(this.MAX_SCALE, this.scale + this.SCALE_STEP); if (newScale !== this.scale) { this._applyZoom(newScale); return true; } return false; } /** * Immediately resets zoom to 100% */ resetZoom() { this._applyZoom(this.MAX_SCALE); } /** * Applies the zoom scale to the canvas * @private */ _applyZoom(newScale) { this.scale = newScale; // Get current translation const transform = window.getComputedStyle(this.canvas).transform; const matrix = new DOMMatrix(transform); const currentX = matrix.m41; const currentY = matrix.m42; // Apply both transforms this.canvas.style.transform = `translate(${currentX}px, ${currentY}px) scale(${this.scale})`; this.canvas.style.transformOrigin = 'center center'; this.requestUpdate(); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('wheel', this._handleWheel); } }; }