From 6d16783d176340af48135cc4ca7a49151723f45d Mon Sep 17 00:00:00 2001 From: 3dtours Date: Thu, 15 Oct 2026 18:25:18 +0700 Subject: [PATCH] fix(studio): the strip stands on the frame in hand when the rail changes width MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A narrower rail is not the wide one with less of the roll in it: its frames are a different number of pixels wide, so the scrollLeft the reader was standing on is a different frame. Opening the studio on a desk and switching to the phone layout left the strip standing further down the roll than the frame on the stage — with three hundred frames in the folder, frame 170 where frame 150 was open — and the frames of the open photo were off the rail. The rail is now put back on the frame in hand whenever its own width changes, the window is read from where it then stands, and the tiles the reader is being shown are the ones asked for. --- docker/frontend/src/ui/Filmstrip.tsx | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/docker/frontend/src/ui/Filmstrip.tsx b/docker/frontend/src/ui/Filmstrip.tsx index 3ad262f..ed28327 100644 --- a/docker/frontend/src/ui/Filmstrip.tsx +++ b/docker/frontend/src/ui/Filmstrip.tsx @@ -92,12 +92,18 @@ export const Filmstrip = memo(function Filmstrip({ // and not `scroll-behavior: smooth` on the rail, which glides across the whole // roll: a scroll event a frame, and a fresh window asked for at each one, of // frames the reader never stops on. The rail is put where it is wanted. - useEffect(() => { + const centre = useCallback(() => { const box = rail.current; const el = box?.children[at] as HTMLElement | undefined; if (!box || !el) return; box.scrollLeft = el.offsetLeft - box.clientWidth / 2 + el.clientWidth / 2; - }, [at, frames]); + }, [at]); + + useEffect(() => { + centre(); + // The frames are in the dependency too: the frame in hand is a child of the + // rail, and until the folder arrives there is nothing to stand on. + }, [centre, frames]); // Scrolling and resizing both move what the rail can reach: the phone's rail // is a different window on the same roll, and the tiles it now needs are not @@ -115,14 +121,23 @@ export const Filmstrip = memo(function Filmstrip({ report(); }); }; + // A narrower rail is not the wide one with less of the roll in it: it is a + // different number of frames to the pixel the reader was standing on, so the + // same scrollLeft is a different frame — the strip left the frame on the + // stage behind and drew the roll from somewhere else down it. It is put back + // on that frame first, and the window is read from where it then stands. + const resized = () => { + centre(); + again(); + }; box.addEventListener('scroll', again, { passive: true }); - const ro = new ResizeObserver(again); + const ro = new ResizeObserver(resized); ro.observe(box); return () => { box.removeEventListener('scroll', again); ro.disconnect(); }; - }, [report]); + }, [report, centre]); return ( // A wheel has no sideways axis, so a plain tick over the roll walks it along.