Merge nucleic/calm-willow-ibis-dozm into main
Mirrored from corpo 5e27961.
This commit is contained in:
+40
-65
@@ -1560,50 +1560,32 @@ const canonical = new URL('/', Astro.site);
|
|||||||
{ capture: true, passive: false },
|
{ capture: true, passive: false },
|
||||||
);
|
);
|
||||||
|
|
||||||
/* One swipe, one product — the finger's version of the rule above, and it has
|
/* One swipe, one product, and nothing else about the swipe is read. A finger
|
||||||
to be taken over rather than damped: the momentum a touch scroller hands back
|
asking for the next product asks the same thing whether it travels an inch or
|
||||||
after the finger has gone is the whole of the problem, since it carries a
|
the length of the screen, so the stack does not follow it: it steps once, the
|
||||||
flick clean across the stack and there is nothing left holding the glass to
|
moment the gesture is unmistakably a swipe, and ignores the rest of it. That
|
||||||
ask it not to. So the swipe is given the product it starts on and the one
|
is also why there is no resistance anywhere below — nothing is tracking the
|
||||||
either side of it, tracks the finger exactly between them, and past them
|
finger, so there is nothing for the reader to push against and nothing to
|
||||||
takes the finger's weight and returns a fifth of it, the way a scroller
|
spring back from. It replaces the native scroller rather than damping it,
|
||||||
resists at its end. Lifting off settles on the product on the stage. A tap is
|
because the momentum a touch scroller hands back after the finger has gone
|
||||||
untouched — nothing is prevented until the finger has actually moved — and so
|
carries a flick clean across the stack and there is nothing left holding the
|
||||||
is a second finger, which is the reader zooming and not scrolling at all. */
|
glass to ask it not to. A tap never reaches the threshold; a second finger is
|
||||||
const TOUCH_STRETCH = 0.2;
|
a reader zooming and not scrolling at all. */
|
||||||
/* And never further than this, however long the swipe goes on. A resistance
|
const SWIPE_MIN_PX = 24;
|
||||||
that only ever divides still arrives at the next product eventually, and the
|
|
||||||
reader sees it take the stage and get taken off again on the way back — the
|
|
||||||
one thing the whole rule is here to prevent. A fifteenth of the screen is far
|
|
||||||
enough to read as give and much nearer than the next card's edge. */
|
|
||||||
const TOUCH_STRETCH_MAX = 0.06;
|
|
||||||
|
|
||||||
|
/* Where the finger went down — the swipe is measured from there rather than
|
||||||
|
from the last move, so a slow drag accumulates to the same threshold a flick
|
||||||
|
crosses at once. */
|
||||||
let touchY: number | null = null;
|
let touchY: number | null = null;
|
||||||
let touchBeat = 0;
|
let touchX = 0;
|
||||||
let touchGive = 0;
|
let touchTaken = false;
|
||||||
/* A tap is a finger that never moved, and settling under one would scroll the
|
|
||||||
page out from under the link being followed. */
|
|
||||||
let touchMoved = false;
|
|
||||||
/* Where the swipe has asked to be, before any of the resistance below: reading
|
|
||||||
it back off the element instead would compound the resistance against itself,
|
|
||||||
and the finger would meet a wall rather than a spring. */
|
|
||||||
let touchTop = 0;
|
|
||||||
let touchLow = 0;
|
|
||||||
let touchHigh = 0;
|
|
||||||
|
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
'touchstart',
|
'touchstart',
|
||||||
(event) => {
|
(event) => {
|
||||||
touchY = event.touches.length === 1 ? event.touches[0]?.clientY ?? null : null;
|
touchY = event.touches.length === 1 ? event.touches[0]?.clientY ?? null : null;
|
||||||
if (touchY === null) return;
|
touchX = event.touches[0]?.clientX ?? 0;
|
||||||
|
touchTaken = false;
|
||||||
measureBeats();
|
|
||||||
touchMoved = false;
|
|
||||||
touchBeat = technologyAtScroll();
|
|
||||||
touchTop = scrollRoot.scrollTop;
|
|
||||||
touchLow = beatRest(touchBeat - 1);
|
|
||||||
touchHigh = beatRest(touchBeat + 1);
|
|
||||||
touchGive = scrollRoot.clientHeight * TOUCH_STRETCH_MAX;
|
|
||||||
},
|
},
|
||||||
{ capture: true, passive: true },
|
{ capture: true, passive: true },
|
||||||
);
|
);
|
||||||
@@ -1611,45 +1593,38 @@ const canonical = new URL('/', Astro.site);
|
|||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
'touchmove',
|
'touchmove',
|
||||||
(event) => {
|
(event) => {
|
||||||
const nextTouchY = event.touches[0]?.clientY;
|
const finger = event.touches[0];
|
||||||
if (touchY === null || nextTouchY === undefined) return;
|
if (touchY === null || !finger) return;
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
touchMoved = true;
|
if (touchTaken) return;
|
||||||
touchTop += touchY - nextTouchY;
|
|
||||||
touchY = nextTouchY;
|
|
||||||
|
|
||||||
const bound = Math.min(touchHigh, Math.max(touchLow, touchTop));
|
const travelled = touchY - finger.clientY;
|
||||||
const past = touchTop - bound;
|
/* More across than down is a reader swiping at the browser rather than at
|
||||||
/* Starts out giving back a fifth and closes on the cap: the same curve a
|
the stack, and the stack is not what should answer. */
|
||||||
scroller bounces with, so the swipe meets a spring rather than a wall. */
|
if (
|
||||||
const give =
|
Math.abs(travelled) < SWIPE_MIN_PX ||
|
||||||
Math.sign(past) *
|
Math.abs(travelled) < Math.abs(touchX - finger.clientX)
|
||||||
touchGive *
|
) {
|
||||||
(1 - 1 / (1 + (TOUCH_STRETCH * Math.abs(past)) / touchGive));
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
scrollRoot.scrollTop = bound + give;
|
touchTaken = true;
|
||||||
|
measureBeats();
|
||||||
|
scrollRoot.scrollTo({
|
||||||
|
top: beatRest(technologyAtScroll() + Math.sign(travelled)),
|
||||||
|
behavior: reducedMotion.matches ? 'auto' : 'smooth',
|
||||||
|
});
|
||||||
},
|
},
|
||||||
{ capture: true, passive: false },
|
{ capture: true, passive: false },
|
||||||
);
|
);
|
||||||
|
|
||||||
/* The stage is asked where it is rather than told, so a swipe that gave up
|
const endTouch = () => {
|
||||||
halfway settles back where it came from — but never further than the one
|
|
||||||
product the swipe was granted, whatever the stretch above let through. */
|
|
||||||
const settleTouch = () => {
|
|
||||||
if (touchY === null) return;
|
|
||||||
touchY = null;
|
touchY = null;
|
||||||
if (!touchMoved) return;
|
|
||||||
|
|
||||||
const beat = Math.min(touchBeat + 1, Math.max(touchBeat - 1, technologyAtScroll()));
|
|
||||||
scrollRoot.scrollTo({
|
|
||||||
top: beatRest(beat),
|
|
||||||
behavior: reducedMotion.matches ? 'auto' : 'smooth',
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('touchend', settleTouch, { capture: true, passive: true });
|
document.addEventListener('touchend', endTouch, { capture: true, passive: true });
|
||||||
document.addEventListener('touchcancel', settleTouch, { capture: true, passive: true });
|
document.addEventListener('touchcancel', endTouch, { capture: true, passive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Where the band below reaches: the story hands over to a card when that card's
|
/* Where the band below reaches: the story hands over to a card when that card's
|
||||||
|
|||||||
Reference in New Issue
Block a user