Turn the phone in one step, not through every shape

A turn is not one shape change: the viewport passes through every shape
between the one it left and the one it lands in, and each of them
re-letterboxes the picture somewhere else. Painted, that run of hard jumps
is the stutter.

So for the length of a turn the slides are held at the size they already
had, and the moment the viewport stops moving they take the size it ended
at -- measured, not guessed. The picture moves exactly once, straight into
the position and size it then keeps. The device's own `orientationchange`
starts the hold, which is the earliest anything can know a turn is coming
and the one signal that separates it from a window being dragged wider;
failing that, the viewport swapping its long side for its short one does. A
viewport that only changes height is the browser's chrome sliding away, and
is never held.

Underneath: snapping is off while the slides are held, so the container
cannot re-anchor on the old height at the same time; a scroll arriving
during the window is put back in the event rather than a frame later, so
the stale position is never painted; and the per-frame realign only writes
what actually differs instead of costing a layout sixty times a second.

Measured over a recorded turn: the picture moves 12 times -> 2, once per
turn; the worst of those was a 660px jump between two intermediate shapes
and is now the one step into the final size; the reel is never off its step
(was 660px out); and the clip never pauses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MPZFnNdHbPGDTqQUNiE4ZN
This commit is contained in:
Simon
2026-10-19 13:40:30 +00:00
parent b9f6a78fdc
commit 6d9cc301a7
3 changed files with 403 additions and 7 deletions

View File

@@ -2236,6 +2236,27 @@ body.feed-mode-open .mode-toggle-btn .icon-svg {
height: 1px;
}
/* A turn goes through every shape between the one it left and the one it
lands in, and each of those re-letterboxes the picture somewhere else --
a run of jumps where there should be one. So for the length of the turn
the slides are held at the size it is heading for (feed.js sets the two
numbers): the picture moves once, straight into its final position and
size, and stays there while the viewport catches up to it. */
.feed-view.is-turning .feed-slide {
width: var(--turn-w);
height: var(--turn-h);
}
.feed-view.is-turning .feed-scroll {
/* The pinned slides are a different shape from the viewport until the turn
finishes, and none of that overflow is for scrolling through. Snapping
is off with it: the container would otherwise be re-anchoring on the old
height at the same time as the feed is pinning itself to the new one. */
overflow: hidden;
scroll-snap-type: none;
scroll-behavior: auto;
}
/* Stands in for the slides above the rendered window so scroll position and
snap points stay stable while slides are virtualized in and out. */
.feed-top-spacer {