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}
-
Click and drag to select • Drag corners to resize • Drag center to move
+
Click and drag to select • Drag corners and edges to resize or double click them to max • Drag center to move
`;
@@ -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';
}