diff --git a/templates/index.html b/templates/index.html index 863f040..467f6eb 100644 --- a/templates/index.html +++ b/templates/index.html @@ -748,7 +748,7 @@

Codec: ${videoInfo.codec}

- +
`; @@ -830,10 +830,26 @@ cropCanvas.addEventListener('mousedown', (e) => { if (!cropMode) return; + e.preventDefault(); const rect = cropCanvas.getBoundingClientRect(); const mouseX = e.clientX - rect.left; const mouseY = e.clientY - rect.top; + handlePointerDown(mouseX, mouseY); + }); + + cropCanvas.addEventListener('touchstart', (e) => { + if (!cropMode) return; + e.preventDefault(); + const rect = cropCanvas.getBoundingClientRect(); + const touch = e.touches[0]; + const mouseX = touch.clientX - rect.left; + const mouseY = touch.clientY - rect.top; + + handlePointerDown(mouseX, mouseY); + }); + + function handlePointerDown(mouseX, mouseY) { // Check if clicking on a handle if (cropRect) { const handle = getHandleAtPosition(mouseX, mouseY); @@ -866,7 +882,7 @@ cropRect = null; startX = mouseX; startY = mouseY; - }); + } cropCanvas.addEventListener('mousemove', (e) => { if (!cropMode) return; @@ -874,6 +890,21 @@ const mouseX = e.clientX - rect.left; const mouseY = e.clientY - rect.top; + handlePointerMove(mouseX, mouseY); + }); + + cropCanvas.addEventListener('touchmove', (e) => { + if (!cropMode) return; + e.preventDefault(); + const rect = cropCanvas.getBoundingClientRect(); + const touch = e.touches[0]; + const mouseX = touch.clientX - rect.left; + const mouseY = touch.clientY - rect.top; + + handlePointerMove(mouseX, mouseY); + }); + + function handlePointerMove(mouseX, mouseY) { // Update cursor based on position if (!isDrawing && !isDraggingHandle && !isDraggingEdge && !isDraggingBox && cropRect) { const handle = getHandleAtPosition(mouseX, mouseY); @@ -924,11 +955,20 @@ }; drawCropRect(); } - }); + } cropCanvas.addEventListener('mouseup', (e) => { if (!cropMode) return; - + handlePointerUp(); + }); + + cropCanvas.addEventListener('touchend', (e) => { + if (!cropMode) return; + e.preventDefault(); + handlePointerUp(); + }); + + function handlePointerUp() { if (isDrawing || isDraggingHandle || isDraggingEdge || isDraggingBox) { updateFormFields(); } @@ -943,20 +983,18 @@ // Keep crop mode active and canvas visible drawCropRect(); - }); + } // Handle mouse leaving canvas while dragging cropCanvas.addEventListener('mouseleave', (e) => { if (isDrawing || isDraggingHandle || isDraggingEdge || isDraggingBox) { - // Finish the drag operation - updateFormFields(); - isDrawing = false; - isDraggingHandle = false; - isDraggingEdge = false; - isDraggingBox = false; - activeHandle = null; - activeEdge = null; - drawCropRect(); + handlePointerUp(); + } + }); + + cropCanvas.addEventListener('touchcancel', (e) => { + if (cropMode) { + handlePointerUp(); } }); @@ -1038,6 +1076,9 @@ function getHandleAtPosition(x, y) { if (!cropRect) return null; + // Larger hit area on mobile (touch targets should be at least 44x44px) + const hitSize = 'ontouchstart' in window ? 22 : handleSize; + const handles = [ { name: 'tl', x: cropRect.x, y: cropRect.y }, { name: 'tr', x: cropRect.x + cropRect.width, y: cropRect.y }, @@ -1046,7 +1087,7 @@ ]; for (const handle of handles) { - if (Math.abs(x - handle.x) <= handleSize && Math.abs(y - handle.y) <= handleSize) { + if (Math.abs(x - handle.x) <= hitSize && Math.abs(y - handle.y) <= hitSize) { return handle.name; } } @@ -1056,30 +1097,32 @@ function getEdgeAtPosition(x, y) { if (!cropRect) return null; - const tolerance = 15; // Click detection tolerance in pixels + // Larger tolerance on mobile for easier touch interaction + const tolerance = 'ontouchstart' in window ? 25 : 15; const { x: rx, y: ry, width: rw, height: rh } = cropRect; + const hitSize = 'ontouchstart' in window ? 22 : handleSize; // 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) { + if (Math.abs(y - ry) <= tolerance && x > rx + hitSize && x < rx + rw - hitSize) { return 'top'; } // Bottom edge (exclude corners) - if (Math.abs(y - (ry + rh)) <= tolerance && x > rx + handleSize && x < rx + rw - handleSize) { + if (Math.abs(y - (ry + rh)) <= tolerance && x > rx + hitSize && x < rx + rw - hitSize) { return 'bottom'; } // Left edge (exclude corners) - if (Math.abs(x - rx) <= tolerance && y > ry + handleSize && y < ry + rh - handleSize) { + if (Math.abs(x - rx) <= tolerance && y > ry + hitSize && y < ry + rh - hitSize) { return 'left'; } // Right edge (exclude corners) - if (Math.abs(x - (rx + rw)) <= tolerance && y > ry + handleSize && y < ry + rh - handleSize) { + if (Math.abs(x - (rx + rw)) <= tolerance && y > ry + hitSize && y < ry + rh - hitSize) { return 'right'; }