83 lines
1.9 KiB
TypeScript
83 lines
1.9 KiB
TypeScript
|
|
import { BaseTool } from './base-tool';
|
||
|
|
import type { PointerState, ToolSettings } from '@/types';
|
||
|
|
import { distance } from '@/lib/utils';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Eraser tool - Remove pixels
|
||
|
|
*/
|
||
|
|
export class EraserTool extends BaseTool {
|
||
|
|
private lastX = 0;
|
||
|
|
private lastY = 0;
|
||
|
|
|
||
|
|
constructor() {
|
||
|
|
super('Eraser');
|
||
|
|
}
|
||
|
|
|
||
|
|
onPointerDown(
|
||
|
|
pointer: PointerState,
|
||
|
|
ctx: CanvasRenderingContext2D,
|
||
|
|
settings: ToolSettings
|
||
|
|
): void {
|
||
|
|
this.isDrawing = true;
|
||
|
|
this.lastX = pointer.x;
|
||
|
|
this.lastY = pointer.y;
|
||
|
|
|
||
|
|
// Erase initial stamp
|
||
|
|
this.eraseStamp(pointer.x, pointer.y, ctx, settings);
|
||
|
|
}
|
||
|
|
|
||
|
|
onPointerMove(
|
||
|
|
pointer: PointerState,
|
||
|
|
ctx: CanvasRenderingContext2D,
|
||
|
|
settings: ToolSettings
|
||
|
|
): void {
|
||
|
|
if (!this.isDrawing) return;
|
||
|
|
|
||
|
|
// Calculate distance from last point
|
||
|
|
const dist = distance(this.lastX, this.lastY, pointer.x, pointer.y);
|
||
|
|
const spacing = settings.size * settings.spacing;
|
||
|
|
|
||
|
|
if (dist >= spacing) {
|
||
|
|
// Interpolate between points for smooth erasing
|
||
|
|
const steps = Math.ceil(dist / spacing);
|
||
|
|
|
||
|
|
for (let i = 1; i <= steps; i++) {
|
||
|
|
const t = i / steps;
|
||
|
|
const x = this.lastX + (pointer.x - this.lastX) * t;
|
||
|
|
const y = this.lastY + (pointer.y - this.lastY) * t;
|
||
|
|
|
||
|
|
this.eraseStamp(x, y, ctx, settings);
|
||
|
|
}
|
||
|
|
|
||
|
|
this.lastX = pointer.x;
|
||
|
|
this.lastY = pointer.y;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
onPointerUp(
|
||
|
|
pointer: PointerState,
|
||
|
|
ctx: CanvasRenderingContext2D,
|
||
|
|
settings: ToolSettings
|
||
|
|
): void {
|
||
|
|
this.isDrawing = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Erase a circular area
|
||
|
|
*/
|
||
|
|
private eraseStamp(
|
||
|
|
x: number,
|
||
|
|
y: number,
|
||
|
|
ctx: CanvasRenderingContext2D,
|
||
|
|
settings: ToolSettings
|
||
|
|
): void {
|
||
|
|
ctx.save();
|
||
|
|
ctx.globalCompositeOperation = 'destination-out';
|
||
|
|
ctx.globalAlpha = settings.opacity;
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.arc(x, y, settings.size / 2, 0, Math.PI * 2);
|
||
|
|
ctx.fill();
|
||
|
|
ctx.restore();
|
||
|
|
}
|
||
|
|
}
|