feat: add interactive output area transform handles

Implemented drag-to-resize functionality for the output area with visual transform handles on corners and edges. Users can now interactively resize the output area by dragging handles instead of using dialogs, with support for grid snapping and aspect ratio preservation.
This commit is contained in:
Dariusz L
2025-08-14 13:54:10 +02:00
parent 7ce7194cbf
commit 0f4f2cb1b0
6 changed files with 495 additions and 161 deletions

View File

@@ -147,6 +147,7 @@ export class CanvasRenderer {
this.renderInteractionElements(ctx);
this.canvas.shapeTool.render(ctx);
this.drawMaskAreaBounds(ctx); // Draw mask area bounds when mask tool is active
this.renderOutputAreaTransformHandles(ctx); // Draw output area transform handles
this.renderLayerInfo(ctx);
// Update custom shape menu position and visibility
if (this.canvas.outputAreaShape) {
@@ -832,4 +833,40 @@ export class CanvasRenderer {
// Just ensure it's the right size
this.updateOverlaySize();
}
/**
* Draw transform handles for output area when in transform mode
*/
renderOutputAreaTransformHandles(ctx) {
if (this.canvas.canvasInteractions.interaction.mode !== 'transformingOutputArea') {
return;
}
const bounds = this.canvas.outputAreaBounds;
const handleRadius = 5 / this.canvas.viewport.zoom;
// Define handle positions
const handles = {
'nw': { x: bounds.x, y: bounds.y },
'n': { x: bounds.x + bounds.width / 2, y: bounds.y },
'ne': { x: bounds.x + bounds.width, y: bounds.y },
'e': { x: bounds.x + bounds.width, y: bounds.y + bounds.height / 2 },
'se': { x: bounds.x + bounds.width, y: bounds.y + bounds.height },
's': { x: bounds.x + bounds.width / 2, y: bounds.y + bounds.height },
'sw': { x: bounds.x, y: bounds.y + bounds.height },
'w': { x: bounds.x, y: bounds.y + bounds.height / 2 },
};
// Draw handles
ctx.fillStyle = '#ffffff';
ctx.strokeStyle = '#000000';
ctx.lineWidth = 1 / this.canvas.viewport.zoom;
for (const [name, pos] of Object.entries(handles)) {
ctx.beginPath();
ctx.arc(pos.x, pos.y, handleRadius, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
}
// Draw a highlight around the output area
ctx.strokeStyle = 'rgba(0, 150, 255, 0.8)';
ctx.lineWidth = 3 / this.canvas.viewport.zoom;
ctx.setLineDash([]);
ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);
}
}