From 8a5822e55659d86a3358b0ef4283aec43838d3fb Mon Sep 17 00:00:00 2001 From: Mike Johnston Date: Mon, 9 Feb 2026 22:42:36 -0500 Subject: [PATCH] snap edges on crop --- templates/index.html | 66 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/templates/index.html b/templates/index.html index 3def55c..f404f54 100644 --- a/templates/index.html +++ b/templates/index.html @@ -544,7 +544,7 @@

- 💡 Draw crop area on video above • Double-click corner handles to snap to edges + 💡 Draw crop area on video above • Double-click edges or corners to snap

@@ -942,6 +942,16 @@ const mouseX = e.clientX - rect.left; const mouseY = e.clientY - rect.top; + // Check for edge click first (between corners) + const edge = getEdgeAtPosition(mouseX, mouseY); + if (edge) { + snapEdgeToSide(edge); + updateFormFields(); + drawCropRect(); + return; + } + + // Fall back to corner handling const handle = getHandleAtPosition(mouseX, mouseY); if (handle) { snapHandleToEdge(handle); @@ -1017,6 +1027,39 @@ return null; } + function getEdgeAtPosition(x, y) { + if (!cropRect) return null; + + const tolerance = 15; // Click detection tolerance in pixels + const { x: rx, y: ry, width: rw, height: rh } = cropRect; + + // Check if within the rectangle bounds (with tolerance) + const inHorizontalRange = x >= rx - tolerance && x <= rx + rw + tolerance; + const inVerticalRange = y >= ry - tolerance && y <= ry + rh + tolerance; + + // Top edge (exclude corners) + if (Math.abs(y - ry) <= tolerance && x > rx + handleSize && x < rx + rw - handleSize) { + return 'top'; + } + + // Bottom edge (exclude corners) + if (Math.abs(y - (ry + rh)) <= tolerance && x > rx + handleSize && x < rx + rw - handleSize) { + return 'bottom'; + } + + // Left edge (exclude corners) + if (Math.abs(x - rx) <= tolerance && y > ry + handleSize && y < ry + rh - handleSize) { + return 'left'; + } + + // Right edge (exclude corners) + if (Math.abs(x - (rx + rw)) <= tolerance && y > ry + handleSize && y < ry + rh - handleSize) { + return 'right'; + } + + return null; + } + function getHandleCursor(handle) { const cursors = { 'tl': 'nw-resize', @@ -1122,6 +1165,27 @@ } } + function snapEdgeToSide(edge) { + if (!cropRect) return; + + switch (edge) { + case 'top': // Snap top edge to y=0 + cropRect.height += cropRect.y; // Extend height by current y + cropRect.y = 0; + break; + case 'bottom': // Snap bottom edge to canvas height + cropRect.height = cropCanvas.height - cropRect.y; + break; + case 'left': // Snap left edge to x=0 + cropRect.width += cropRect.x; // Extend width by current x + cropRect.x = 0; + break; + case 'right': // Snap right edge to canvas width + cropRect.width = cropCanvas.width - cropRect.x; + break; + } + } + function updateFormFields() { if (!cropRect) return;