import { BaseTool } from './base-tool'; import type { PointerState, ToolSettings } from '@/types'; export type GradientType = 'linear' | 'radial' | 'angular'; /** * Gradient tool - Linear, Radial, and Angular gradients */ export class GradientTool extends BaseTool { private startX = 0; private startY = 0; private previewCanvas: HTMLCanvasElement | null = null; constructor() { super('Gradient'); } onPointerDown( pointer: PointerState, ctx: CanvasRenderingContext2D, settings: ToolSettings ): void { this.isDrawing = true; this.startX = pointer.x; this.startY = pointer.y; // Create preview canvas this.previewCanvas = document.createElement('canvas'); this.previewCanvas.width = ctx.canvas.width; this.previewCanvas.height = ctx.canvas.height; } onPointerMove( pointer: PointerState, ctx: CanvasRenderingContext2D, settings: ToolSettings ): void { if (!this.isDrawing || !this.previewCanvas) return; const previewCtx = this.previewCanvas.getContext('2d'); if (!previewCtx) return; // Clear preview previewCtx.clearRect(0, 0, this.previewCanvas.width, this.previewCanvas.height); // Draw gradient preview this.drawGradient( previewCtx, this.startX, this.startY, pointer.x, pointer.y, settings.color, settings.secondaryColor || '#ffffff', settings.gradientType || 'linear' ); // Draw preview on main canvas (non-destructive) 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.globalAlpha = 0.7; ctx.drawImage(this.previewCanvas, 0, 0); ctx.globalAlpha = 1.0; } onPointerUp( pointer: PointerState, ctx: CanvasRenderingContext2D, settings: ToolSettings ): void { if (!this.isDrawing) return; // Apply final gradient this.drawGradient( ctx, this.startX, this.startY, pointer.x, pointer.y, settings.color, settings.secondaryColor || '#ffffff', settings.gradientType || 'linear' ); this.isDrawing = false; this.previewCanvas = null; } private drawGradient( ctx: CanvasRenderingContext2D, startX: number, startY: number, endX: number, endY: number, color1: string, color2: string, type: GradientType ): void { let gradient: CanvasGradient; if (type === 'linear') { gradient = ctx.createLinearGradient(startX, startY, endX, endY); gradient.addColorStop(0, color1); gradient.addColorStop(1, color2); } else if (type === 'radial') { const radius = Math.sqrt( Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2) ); gradient = ctx.createRadialGradient(startX, startY, 0, startX, startY, radius); gradient.addColorStop(0, color1); gradient.addColorStop(1, color2); } else if (type === 'angular' && 'createConicGradient' in ctx) { // Angular gradient using conic gradient (if supported) const angle = Math.atan2(endY - startY, endX - startX); // @ts-ignore - createConicGradient might not be in all browsers gradient = ctx.createConicGradient(angle, startX, startY); gradient.addColorStop(0, color1); gradient.addColorStop(0.5, color2); gradient.addColorStop(1, color1); } else { // Fallback to radial for angular without conic gradient support const radius = Math.sqrt( Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2) ); gradient = ctx.createRadialGradient(startX, startY, 0, startX, startY, radius); gradient.addColorStop(0, color1); gradient.addColorStop(1, color2); } ctx.fillStyle = gradient; ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); } getCursor(): string { return 'crosshair'; } }