fix: prevent pointer events from hijacking two-finger touch pan
On touch devices, both pointer and touch events fire. When a second finger was added, the pointer handler re-captured the interaction, fighting the touch-based pan/pinch. Now the touch handler releases pointer captures and sets a flag that blocks the pointer handler during two-finger gestures. Also cancels shape drag on multi-touch and closes the context menu on touchstart for reliable mobile dismiss. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
658eb966d6
commit
5e0f30567a
|
|
@ -438,6 +438,16 @@ export class FolkShape extends FolkElement {
|
|||
const isDragHandle = target?.closest?.(".header, [data-drag]") !== null;
|
||||
const isValidDragTarget = target === this || isDragHandle;
|
||||
|
||||
// Two-finger gesture → cancel shape drag so canvas pan takes over
|
||||
if (event.touches.length >= 2) {
|
||||
if (this.#isTouchDragging) {
|
||||
this.#lastTouchPos = null;
|
||||
this.#isTouchDragging = false;
|
||||
this.#internals.states.delete("move");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.type === "touchstart" && event.touches.length === 1) {
|
||||
if (!isValidDragTarget) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -2778,11 +2778,17 @@
|
|||
if (memoryPanel.classList.contains("open")) renderMemoryPanel();
|
||||
});
|
||||
|
||||
// Close context menu on click elsewhere
|
||||
// Close context menu on click/touch elsewhere
|
||||
document.addEventListener("click", () => {
|
||||
shapeContextMenu.classList.remove("open");
|
||||
contextShapeId = null;
|
||||
});
|
||||
document.addEventListener("touchstart", (e) => {
|
||||
if (!e.target.closest("#shape-context-menu")) {
|
||||
shapeContextMenu.classList.remove("open");
|
||||
contextShapeId = null;
|
||||
}
|
||||
});
|
||||
|
||||
// Memory panel — browse and remember forgotten shapes
|
||||
const memoryPanel = document.getElementById("memory-panel");
|
||||
|
|
@ -3100,6 +3106,7 @@
|
|||
// Touch gesture handling for two-finger pan + pinch-to-zoom
|
||||
let lastTouchCenter = null;
|
||||
let lastTouchDist = null;
|
||||
let isTouchPanning = false;
|
||||
|
||||
function getTouchCenter(touches) {
|
||||
return {
|
||||
|
|
@ -3117,9 +3124,17 @@
|
|||
canvas.addEventListener("touchstart", (e) => {
|
||||
if (e.touches.length === 2) {
|
||||
e.preventDefault();
|
||||
// Cancel any single-finger pan to avoid conflict
|
||||
isTouchPanning = true;
|
||||
// Release any captured pointer so pointer events stop competing
|
||||
if (panPointerId !== null) {
|
||||
try { canvas.releasePointerCapture(panPointerId); } catch {}
|
||||
}
|
||||
// Cancel pointer-based pan state completely
|
||||
isPanning = false;
|
||||
panPointerId = null;
|
||||
interactionMode = "none";
|
||||
clearTimeout(holdTimer);
|
||||
holdTimer = null;
|
||||
canvas.style.cursor = "";
|
||||
lastTouchCenter = getTouchCenter(e.touches);
|
||||
lastTouchDist = getTouchDist(e.touches);
|
||||
|
|
@ -3127,7 +3142,7 @@
|
|||
}, { passive: false });
|
||||
|
||||
canvas.addEventListener("touchmove", (e) => {
|
||||
if (e.touches.length === 2) {
|
||||
if (e.touches.length === 2 && isTouchPanning) {
|
||||
e.preventDefault();
|
||||
|
||||
const currentCenter = getTouchCenter(e.touches);
|
||||
|
|
@ -3163,6 +3178,7 @@
|
|||
if (e.touches.length < 2) {
|
||||
lastTouchCenter = null;
|
||||
lastTouchDist = null;
|
||||
isTouchPanning = false;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -3242,6 +3258,7 @@
|
|||
const PAN_THRESHOLD = 4; // px movement to confirm pan intent
|
||||
|
||||
canvas.addEventListener("pointerdown", (e) => {
|
||||
if (isTouchPanning) return; // two-finger gesture owns the canvas
|
||||
if (e.target !== canvas && e.target !== canvasContent) return;
|
||||
if (connectMode) return;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue