diff --git a/tools/crop-tool.ts b/tools/crop-tool.ts index 43e8a57..bc70676 100644 --- a/tools/crop-tool.ts +++ b/tools/crop-tool.ts @@ -11,41 +11,28 @@ export class CropTool extends BaseTool { private activeHandle: string | null = null; private startX = 0; private startY = 0; - private overlayCanvas: HTMLCanvasElement | null = null; private handleSize = 8; + private initialized = false; constructor() { super('Crop'); } - onActivate(): void { - // Initialize crop rect to full canvas size when activated - if (this.overlayCanvas) { - this.cropRect = { - x: 0, - y: 0, - width: this.overlayCanvas.width, - height: this.overlayCanvas.height, - }; - } - } - onPointerDown( pointer: PointerState, ctx: CanvasRenderingContext2D, settings: ToolSettings ): void { - // Create overlay canvas if not exists - if (!this.overlayCanvas) { - this.overlayCanvas = document.createElement('canvas'); - this.overlayCanvas.width = ctx.canvas.width; - this.overlayCanvas.height = ctx.canvas.height; + // Initialize crop rect to full canvas on first use + if (!this.initialized) { this.cropRect = { x: 0, y: 0, width: ctx.canvas.width, height: ctx.canvas.height, }; + this.initialized = true; + this.drawCropOverlay(ctx); } // Check if clicking on a resize handle @@ -236,30 +223,25 @@ export class CropTool extends BaseTool { } private drawCropOverlay(ctx: CanvasRenderingContext2D): void { - if (!this.overlayCanvas) return; - - const overlayCtx = this.overlayCanvas.getContext('2d'); - if (!overlayCtx) return; - - // Clear overlay - overlayCtx.clearRect(0, 0, this.overlayCanvas.width, this.overlayCanvas.height); + // Save context state + ctx.save(); // Draw darkened areas outside crop rect - overlayCtx.fillStyle = 'rgba(0, 0, 0, 0.5)'; + ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; // Top - overlayCtx.fillRect(0, 0, this.overlayCanvas.width, this.cropRect.y); + ctx.fillRect(0, 0, ctx.canvas.width, this.cropRect.y); // Bottom - overlayCtx.fillRect( + ctx.fillRect( 0, this.cropRect.y + this.cropRect.height, - this.overlayCanvas.width, - this.overlayCanvas.height - (this.cropRect.y + this.cropRect.height) + ctx.canvas.width, + ctx.canvas.height - (this.cropRect.y + this.cropRect.height) ); // Left - overlayCtx.fillRect( + ctx.fillRect( 0, this.cropRect.y, this.cropRect.x, @@ -267,17 +249,17 @@ export class CropTool extends BaseTool { ); // Right - overlayCtx.fillRect( + ctx.fillRect( this.cropRect.x + this.cropRect.width, this.cropRect.y, - this.overlayCanvas.width - (this.cropRect.x + this.cropRect.width), + ctx.canvas.width - (this.cropRect.x + this.cropRect.width), this.cropRect.height ); // Draw crop rect border - overlayCtx.strokeStyle = '#ffffff'; - overlayCtx.lineWidth = 2; - overlayCtx.strokeRect( + ctx.strokeStyle = '#ffffff'; + ctx.lineWidth = 2; + ctx.strokeRect( this.cropRect.x, this.cropRect.y, this.cropRect.width, @@ -285,39 +267,39 @@ export class CropTool extends BaseTool { ); // Draw rule of thirds grid - overlayCtx.strokeStyle = 'rgba(255, 255, 255, 0.5)'; - overlayCtx.lineWidth = 1; + ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)'; + ctx.lineWidth = 1; // Vertical lines - overlayCtx.beginPath(); - overlayCtx.moveTo(this.cropRect.x + this.cropRect.width / 3, this.cropRect.y); - overlayCtx.lineTo(this.cropRect.x + this.cropRect.width / 3, this.cropRect.y + this.cropRect.height); - overlayCtx.moveTo(this.cropRect.x + (this.cropRect.width * 2) / 3, this.cropRect.y); - overlayCtx.lineTo(this.cropRect.x + (this.cropRect.width * 2) / 3, this.cropRect.y + this.cropRect.height); - overlayCtx.stroke(); + ctx.beginPath(); + ctx.moveTo(this.cropRect.x + this.cropRect.width / 3, this.cropRect.y); + ctx.lineTo(this.cropRect.x + this.cropRect.width / 3, this.cropRect.y + this.cropRect.height); + ctx.moveTo(this.cropRect.x + (this.cropRect.width * 2) / 3, this.cropRect.y); + ctx.lineTo(this.cropRect.x + (this.cropRect.width * 2) / 3, this.cropRect.y + this.cropRect.height); + ctx.stroke(); // Horizontal lines - overlayCtx.beginPath(); - overlayCtx.moveTo(this.cropRect.x, this.cropRect.y + this.cropRect.height / 3); - overlayCtx.lineTo(this.cropRect.x + this.cropRect.width, this.cropRect.y + this.cropRect.height / 3); - overlayCtx.moveTo(this.cropRect.x, this.cropRect.y + (this.cropRect.height * 2) / 3); - overlayCtx.lineTo(this.cropRect.x + this.cropRect.width, this.cropRect.y + (this.cropRect.height * 2) / 3); - overlayCtx.stroke(); + ctx.beginPath(); + ctx.moveTo(this.cropRect.x, this.cropRect.y + this.cropRect.height / 3); + ctx.lineTo(this.cropRect.x + this.cropRect.width, this.cropRect.y + this.cropRect.height / 3); + ctx.moveTo(this.cropRect.x, this.cropRect.y + (this.cropRect.height * 2) / 3); + ctx.lineTo(this.cropRect.x + this.cropRect.width, this.cropRect.y + (this.cropRect.height * 2) / 3); + ctx.stroke(); // Draw resize handles const handles = this.getHandlePositions(); - overlayCtx.fillStyle = '#ffffff'; - overlayCtx.strokeStyle = '#000000'; - overlayCtx.lineWidth = 1; + ctx.fillStyle = '#ffffff'; + ctx.strokeStyle = '#000000'; + ctx.lineWidth = 1; for (const pos of Object.values(handles)) { - overlayCtx.fillRect( + ctx.fillRect( pos.x - this.handleSize / 2, pos.y - this.handleSize / 2, this.handleSize, this.handleSize ); - overlayCtx.strokeRect( + ctx.strokeRect( pos.x - this.handleSize / 2, pos.y - this.handleSize / 2, this.handleSize, @@ -325,11 +307,8 @@ export class CropTool extends BaseTool { ); } - // Draw overlay on main canvas - const imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); - ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); - ctx.putImageData(imageData, 0, 0); - ctx.drawImage(this.overlayCanvas, 0, 0); + // Restore context state + ctx.restore(); } getCursor(): string { @@ -371,6 +350,6 @@ export class CropTool extends BaseTool { width: ctx.canvas.width, height: ctx.canvas.height, }; - this.overlayCanvas = null; + this.initialized = true; } }