feat(userscript): scroll or reposition from the widened tap strip
touch-action is resolved at gesture start, so the strip cannot be both browser-scrolled and script-dragged. Own the gesture and split by intent: a swipe from the strip scrolls the page, a ~400ms hold arms a reposition drag, and the visible sliver still drags with no hold. Adds the missing pointercancel reset.
This commit is contained in:
@@ -904,6 +904,7 @@
|
||||
const FAB_KEY = "mangabm:fabpos";
|
||||
const FAB_MARGIN = 12; // vertical breathing room at the top and bottom
|
||||
const FAB_EDGE = 0; // horizontal: an edge tab sits flush against the side
|
||||
const ARM_MS = 400; // hold this long on the invisible strip to arm a drag
|
||||
|
||||
function clamp(v, lo, hi) {
|
||||
return Math.max(lo, Math.min(hi, v));
|
||||
@@ -947,23 +948,69 @@
|
||||
applyFabPos(fab, side, y);
|
||||
}
|
||||
|
||||
// A gesture that starts on the visible sliver drags immediately, as it always
|
||||
// has. A gesture that starts on the invisible #hit strip scrolls the page
|
||||
// instead — unless it is held for ARM_MS first, which arms a reposition drag.
|
||||
// ponytail: the scroll is hand-rolled (no momentum/fling) and assumes the
|
||||
// document is the scroller. That is the price of touch-action:none, which we
|
||||
// need so a hold can claim pixels a swipe would otherwise scroll. If the
|
||||
// stiffness annoys, give #hit touch-action:pan-y and accept that vertical
|
||||
// repositioning must start on the 7px sliver.
|
||||
function makeDraggable(fab) {
|
||||
let down = false, moved = false, sx = 0, sy = 0, offX = 0, offY = 0;
|
||||
let down = false, moved = false, armed = false, scrolling = false, fromHit = false;
|
||||
let sx = 0, sy = 0, offX = 0, offY = 0, lastY = 0, armTimer = 0;
|
||||
|
||||
function reset() {
|
||||
down = moved = armed = scrolling = fromHit = false;
|
||||
clearTimeout(armTimer);
|
||||
fab.classList.remove("armed");
|
||||
fab.style.transition = "";
|
||||
}
|
||||
|
||||
function scrollPage(e) {
|
||||
e.preventDefault();
|
||||
window.scrollBy(0, lastY - e.clientY);
|
||||
lastY = e.clientY;
|
||||
}
|
||||
|
||||
fab.addEventListener("pointerdown", (e) => {
|
||||
delete fab.dataset.dragged; // stale flag would swallow this gesture's click
|
||||
down = true;
|
||||
moved = false;
|
||||
armed = false;
|
||||
scrolling = false;
|
||||
fromHit = e.target.id === "hit";
|
||||
sx = e.clientX;
|
||||
sy = e.clientY;
|
||||
lastY = e.clientY;
|
||||
const r = fab.getBoundingClientRect();
|
||||
offX = e.clientX - r.left;
|
||||
offY = e.clientY - r.top;
|
||||
fab.setPointerCapture(e.pointerId);
|
||||
fab.style.transition = "none";
|
||||
clearTimeout(armTimer);
|
||||
armTimer = setTimeout(() => {
|
||||
armed = true;
|
||||
fab.classList.add("armed"); // visible confirmation the hold took
|
||||
}, ARM_MS);
|
||||
});
|
||||
|
||||
fab.addEventListener("pointermove", (e) => {
|
||||
if (!down) return;
|
||||
if (!moved && (Math.abs(e.clientX - sx) > 6 || Math.abs(e.clientY - sy) > 6)) moved = true;
|
||||
if (!moved) return;
|
||||
if (scrolling) {
|
||||
scrollPage(e);
|
||||
return;
|
||||
}
|
||||
if (!moved) {
|
||||
if (Math.abs(e.clientX - sx) <= 6 && Math.abs(e.clientY - sy) <= 6) return;
|
||||
clearTimeout(armTimer);
|
||||
if (fromHit && !armed) {
|
||||
scrolling = true;
|
||||
scrollPage(e);
|
||||
return;
|
||||
}
|
||||
moved = true;
|
||||
}
|
||||
e.preventDefault();
|
||||
const w = fab.offsetWidth, h = fab.offsetHeight;
|
||||
fab.style.left = clamp(e.clientX - offX, 0, window.innerWidth - w) + "px";
|
||||
@@ -971,11 +1018,16 @@
|
||||
fab.style.top = clamp(e.clientY - offY, 0, window.innerHeight - h) + "px";
|
||||
fab.style.bottom = "auto";
|
||||
});
|
||||
|
||||
fab.addEventListener("pointerup", (e) => {
|
||||
if (!down) return;
|
||||
down = false;
|
||||
fab.style.transition = "";
|
||||
if (!moved) return; // tap — let the click handler toggle the panel
|
||||
const didDrag = moved, didScroll = scrolling;
|
||||
reset();
|
||||
if (didScroll) {
|
||||
fab.dataset.dragged = "1"; // suppress any click trailing the scroll
|
||||
return;
|
||||
}
|
||||
if (!didDrag) return; // tap — let the click handler toggle the panel
|
||||
fab.dataset.dragged = "1"; // suppress the click that trails this drag
|
||||
const w = fab.offsetWidth, h = fab.offsetHeight;
|
||||
const r = fab.getBoundingClientRect();
|
||||
@@ -985,6 +1037,8 @@
|
||||
applyFabPos(fab, side, y);
|
||||
saveFabPos(side, y / window.innerHeight);
|
||||
});
|
||||
|
||||
fab.addEventListener("pointercancel", reset);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
Reference in New Issue
Block a user