fix(h5): prevent native scrolling from stealing diagonal image swipes

This commit is contained in:
xsl
2026-10-13 00:42:01 +08:00
parent 4c478b16e3
commit 98935dd168
10 changed files with 197 additions and 171 deletions
+26 -11
View File
@@ -18,6 +18,20 @@ window.SwipeTrack = (function () {
if (instant) { void track.offsetWidth; track.style.transition = ''; }
}
function cancel() { go(index, true); if (options.onCancel) options.onCancel(index); }
function lockDirection(dx, dy) {
if (drag.vertical) return false;
if (drag.horizontal) return true;
var threshold = options.startThreshold || 8, ratio = options.directionRatio || 1;
if (Math.max(Math.abs(dx), Math.abs(dy)) <= threshold) return false;
// Touch and pointer events must share the same decision, regardless of
// which event stream the browser dispatches first.
if (Math.abs(dy) * ratio >= Math.abs(dx)) { drag.vertical = true; return false; }
drag.horizontal = true;
if (options.onStart) options.onStart(index);
viewport.classList.add('dragging'); track.classList.add('dragging');
try { target.setPointerCapture(drag.id); } catch (_) {}
return true;
}
target.addEventListener('pointerdown', function (e) {
if (drag.id != null || e.isPrimary === false || e.button !== 0 || !viewport.clientWidth) return;
suppressUntil = 0; // A fresh tap is intentional, not the preceding drag's click.
@@ -31,17 +45,7 @@ window.SwipeTrack = (function () {
target.addEventListener('pointermove', function (e) {
if (e.pointerId !== drag.id || drag.vertical) return;
var dx = e.clientX - drag.startX, dy = e.clientY - drag.startY;
if (!drag.horizontal) {
var threshold = options.startThreshold || 8, ratio = options.directionRatio || 1;
if (Math.max(Math.abs(dx), Math.abs(dy)) <= threshold) return;
// Each caller chooses its diagonal bias. Once locked, neither direction
// can steal this gesture before the finger lifts.
if (Math.abs(dy) * ratio >= Math.abs(dx)) { drag.vertical = true; return; }
drag.horizontal = true;
if (options.onStart) options.onStart(index);
viewport.classList.add('dragging'); track.classList.add('dragging');
try { target.setPointerCapture(e.pointerId); } catch (_) {}
}
if (!lockDirection(dx, dy)) return;
if (e.cancelable) e.preventDefault();
var elapsed = e.timeStamp - drag.lastAt;
if (elapsed > 0) drag.velocity = (e.clientX - drag.lastX) / elapsed;
@@ -53,6 +57,17 @@ window.SwipeTrack = (function () {
track.style.transform = 'translate3d(' + offset + 'px,0,0)';
if (options.onMove) options.onMove(Math.max(0, Math.min(options.count - 1, -offset / drag.width)));
});
if (options.preventNativeHorizontalPan) {
// preventDefault on pointermove does not stop native viewport panning.
// Cancel only the single-touch horizontal gesture before the browser
// starts scrolling and sends pointercancel. Vertical/text/multi-touch
// gestures keep their native behavior; taps still open image zoom.
target.addEventListener('touchmove', function (e) {
if (drag.id == null || e.touches.length !== 1) return;
var touch = e.touches[0];
if (lockDirection(touch.clientX - drag.startX, touch.clientY - drag.startY) && e.cancelable) e.preventDefault();
}, { passive: false });
}
window.addEventListener('pointerup', function (e) {
if (e.pointerId !== drag.id) return;
var horizontal = drag.horizontal, dx = e.clientX - drag.startX, next = index;