@@ -27871,7 +27871,7 @@ installImportMetaCssBuild(import.meta);
2787127871
2787227872/**
2787327873 * The window two pages are seen through while one replaces the other, measured
27874- * once and published for the length of the movement.
27874+ * at the one moment both exist and published for the length of the movement.
2787527875 *
2787627876 * Both ways of moving from one route to another need the same numbers, so they
2787727877 * are written under the same names and read by the same CSS formulas —
@@ -27896,6 +27896,18 @@ installImportMetaCssBuild(import.meta);
2789627896 * in the window — one page is scrolled and the other is not, one has a top
2789727897 * bar over it and the other has the screen. Each picture is placed at its
2789827898 * own corner inside the window.
27899+ * - **where the arriving state IS, frame after frame.** The browser places the
27900+ * group from the live arriving element on every frame of the movement — that
27901+ * is what makes a picture pair follow an element that moves — and the
27902+ * formula that moves the group back to the window's corner cancels that
27903+ * placement with this corner. Read once, the two stop cancelling the moment
27904+ * anything scrolls the document under the movement (a slice landing and
27905+ * putting a row back, a list placing its default row, a height growing under
27906+ * a restored offset): the window drifts by as much, and both pictures with
27907+ * it — the photograph of the page being left included. So this corner alone
27908+ * is kept live, as often as the browser reads its side of it. It is also
27909+ * the arriving picture's own corner, which then stays where the page really
27910+ * is and lands with no jump when the pictures are dropped.
2789927911 * - **the band the state being left kept free**, next to the one the arriving
2790027912 * state keeps free. A band is furniture — a fixed bar, a sticky row — and
2790127913 * the pictures are cut at it so they are not watched painting over it. Read
@@ -27905,8 +27917,11 @@ installImportMetaCssBuild(import.meta);
2790527917 * at the one moment the state being left still exists, the cut can be taken
2790627918 * at what is furniture on BOTH sides (see the clip formulas).
2790727919 *
27908- * Only the measuring needs JS, and only for the one moment both states exist:
27909- * the page arriving is in the DOM and the transition has not started playing.
27920+ * Only the measuring needs JS, and apart from the arriving corner it happens
27921+ * at the one moment both states exist: the page arriving is in the DOM and the
27922+ * transition has not started playing. The window, the corner of the state
27923+ * being left and its band describe a state that no longer exists, and reading
27924+ * them again is what must never happen.
2791027925 * Everything DERIVED from these numbers — the band a fixed bar covers, how far
2791127926 * a page travels — is derived in CSS, so the application's own numbers (the
2791227927 * room its bars give back, see layout/safe_area.js; what covers the box from
@@ -28007,6 +28022,8 @@ const WINDOW_PROPERTIES = [
2800728022// after another has replaced it must not wipe numbers the new one is standing
2800828023// on.
2800928024let windowOwner = null;
28025+ // Stops re-reading the arriving state's corner, when a movement is on.
28026+ let unfollowArrivingState = null;
2801028027
2801128028/**
2801228029 * The state being left, taken while rendering is held — the page arriving is
@@ -28052,6 +28069,7 @@ const holdTransitionWindow = (owner, element, stateBefore) => {
2805228069 style.setProperty(OLD_BAND_RIGHT_PROPERTY, `${oldBand.right}px`);
2805328070 style.setProperty(OLD_BAND_BOTTOM_PROPERTY, `${oldBand.bottom}px`);
2805428071 style.setProperty(OLD_BAND_LEFT_PROPERTY, `${oldBand.left}px`);
28072+ followArrivingState(element, rectAfter);
2805528073};
2805628074
2805728075// The live layout takes the box back. A discontinuity by construction — the
@@ -28063,12 +28081,45 @@ const releaseTransitionWindow = (owner) => {
2806328081 return;
2806428082 }
2806528083 windowOwner = null;
28084+ if (unfollowArrivingState) {
28085+ unfollowArrivingState();
28086+ }
2806628087 const { style } = document.documentElement;
2806728088 for (const property of WINDOW_PROPERTIES) {
2806828089 style.removeProperty(property);
2806928090 }
2807028091};
2807128092
28093+ // The arriving state's corner, re-read every frame — as often as the browser
28094+ // refreshes the group's placement from the same element (see the top of the
28095+ // file). Requested, never awaited: the first call runs inside the update
28096+ // callback, where a frame cannot come. Written only when it moved: a custom
28097+ // property set on the root recomputes the style of the whole document.
28098+ const followArrivingState = (element, rectAtHold) => {
28099+ if (unfollowArrivingState) {
28100+ unfollowArrivingState();
28101+ }
28102+ const { style } = document.documentElement;
28103+ let top = rectAtHold.top;
28104+ let left = rectAtHold.left;
28105+ let frame = requestAnimationFrame(function read() {
28106+ const rect = element.getBoundingClientRect();
28107+ if (rect.top !== top) {
28108+ top = rect.top;
28109+ style.setProperty(WINDOW_NEW_TOP_PROPERTY, `${top}px`);
28110+ }
28111+ if (rect.left !== left) {
28112+ left = rect.left;
28113+ style.setProperty(WINDOW_NEW_LEFT_PROPERTY, `${left}px`);
28114+ }
28115+ frame = requestAnimationFrame(read);
28116+ });
28117+ unfollowArrivingState = () => {
28118+ unfollowArrivingState = null;
28119+ cancelAnimationFrame(frame);
28120+ };
28121+ };
28122+
2807228123// A band nothing can be wider than, published for an edge nothing is known
2807328124// about: taken as the smaller of the two, it leaves the live band standing on
2807428125// its own, which is the movement navi played before it knew any of this.
@@ -76098,6 +76149,14 @@ function WheelUI(props) {
7609876149 if (trackedItemsRef.current.length === 0) {
7609976150 return;
7610076151 }
76152+ // Without a box (display: none, rows not laid out yet) every position below
76153+ // multiplies by an item size of 0: nothing can be placed. Leave
76154+ // centeredIndexRef null rather than claim a row is centered, so the render
76155+ // that brings the wheel back on screen doesn't read it as "already there".
76156+ if (getItemSize(viewportEl) === 0) {
76157+ centeredIndexRef.current = null;
76158+ return;
76159+ }
7610176160 let selectedIndex = getIndexForValue(currentValueRef.current);
7610276161 if (selectedIndex < 0) {
7610376162 selectedIndex = 0;
@@ -76144,7 +76203,7 @@ function WheelUI(props) {
7614476203 const requestedBehavior = pendingBehaviorRef.current;
7614576204 pendingBehaviorRef.current = null;
7614676205 const viewportEl = getViewport();
76147- if (!viewportEl || viewportEl.offsetParent === null ) {
76206+ if (!viewportEl) {
7614876207 return;
7614976208 }
7615076209 let behavior = "auto";
0 commit comments