artist hero: the blurred layers ride with the photograph
Two layers were left behind when the hero rides up on scroll, both of them blurred copies of it, so both read as "the blur does not line up". The load seed and its mono twin were never in the squeeze-ride rule -- they got a filter on scroll and no transform at all, so at scrollTop 600 they sat 132px below where the photograph had gone. They were left out because hero-squeeze-ride sets transform outright and would drop the scale(1.06) they are built on; hero-seed-ride is the same translate composed AHEAD of that scale, so the offset stays in unscaled pixels and matches the banner's rather than being 1.06x it. Measured 0px drift at scrollTop 0, 300, 600 and 900. The mono twin is a BAKED copy of the hero canvas and snapshotGpuTwins only ever ran on a hero READY. So after any resize the live hero repainted at the new size while the twin went on stretching its old frame into the new box -- an overlay visibly off the photograph, and permanent until the next hero happened to re-bake it. It re-bakes when the measured box changes now, debounced past the renderer's own settle so it copies the canvas already repainted at the new size, and dimension-guarded so it costs nothing during the moving part of a drag. Verified: live store and baked twin agree at 3186x1629, then both at 2856x1607, then both back, with one frame throughout.
This commit is contained in:
1 parent
ae99757ddd
commit
9dc3eaf939
1 file changed
+70
-2
@@ -322,6 +322,40 @@
|
||||
}
|
||||
}
|
||||
|
||||
/** Re-bake the twins when the hero's box has changed size under them.
|
||||
*
|
||||
* The twin is a BAKED copy of the hero canvas, so it keeps the dimensions
|
||||
* it was baked at. snapshotGpuTwins only ever ran on a hero READY, so after
|
||||
* a window or panel resize the live hero repainted at the new size while
|
||||
* the twin went on stretching its old frame into the new box — the mono
|
||||
* overlay sat visibly off the photograph, and stayed there until the next
|
||||
* hero happened to re-bake it. (Kyle: "it happens on resize and it's
|
||||
* permanent until the hero changes.")
|
||||
*
|
||||
* Dimension-guarded rather than fired on every resize tick, so it costs
|
||||
* nothing when the box moves without the backing store following — which
|
||||
* is most of a drag, since the renderer only re-sizes its store once the
|
||||
* box settles. */
|
||||
function rebakeTwinsIfStale() {
|
||||
const source = document.querySelector(".artist-page .gpu-hero canvas.banner.current");
|
||||
const baked = gpuTwinBox?.lastElementChild;
|
||||
if (!source || !source.width || !baked) return;
|
||||
if (baked.width === source.width && baked.height === source.height) return;
|
||||
snapshotGpuTwins();
|
||||
}
|
||||
|
||||
let twinRebakeTimer = 0;
|
||||
$effect(() => {
|
||||
// Re-runs whenever the hero's measured box changes.
|
||||
const width = heroWidth;
|
||||
if (!width) return;
|
||||
clearTimeout(twinRebakeTimer);
|
||||
// Past the renderer's own 140ms settle, so the canvas being copied is
|
||||
// the one already repainted at the new size.
|
||||
twinRebakeTimer = setTimeout(rebakeTwinsIfStale, 260);
|
||||
return () => clearTimeout(twinRebakeTimer);
|
||||
});
|
||||
|
||||
const HERO_ROTATE_MS = 20_000;
|
||||
const HERO_FADE_MS = 1_400;
|
||||
// The preview→finished 4K swap gets its own, shorter dissolve: long enough
|
||||
@@ -5416,6 +5450,27 @@
|
||||
animation-range: 0px var(--travel, 999999px);
|
||||
}
|
||||
|
||||
/* The blurred load seed and its mono twin ride with the photograph.
|
||||
They were left out of the rule above because hero-squeeze-ride sets
|
||||
`transform` outright and would drop the scale(1.06) they are built
|
||||
on; hero-seed-ride is the same translate composed AHEAD of that
|
||||
scale, so the offset stays in unscaled pixels and matches the
|
||||
banner's exactly.
|
||||
|
||||
Without it the seed held its rest position while the hero rode up —
|
||||
measured 132px adrift at scrollTop 600 — so whenever the seed was on
|
||||
screen at a non-zero scroll the blur sat off the photograph. It
|
||||
appeared to "heal itself" only because a new hero hides the seed. */
|
||||
.artist-page[data-scroll="1"] .hero-seed,
|
||||
.artist-page[data-scroll="1"] .seed-mono-twin {
|
||||
animation-name: hero-seed-ride;
|
||||
animation-duration: auto;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: both;
|
||||
animation-timeline: --artist-pane;
|
||||
animation-range: 0px var(--travel, 999999px);
|
||||
}
|
||||
|
||||
/* The seam tail is glued to the banner's edge: SAME squeeze ride,
|
||||
SAME gate, so they attach on the same flip and move as one. (No
|
||||
mono ride — the tail is the artwork's own colour by design and
|
||||
@@ -5481,8 +5536,9 @@
|
||||
animation-range: 0px var(--travel, 999999px);
|
||||
}
|
||||
|
||||
/* (.hero-seed rides nothing here: its mono look is the seed twin's
|
||||
crossfade below, exactly like the banner's.) */
|
||||
/* (.hero-seed's MONO look rides nothing here: that is the seed twin's
|
||||
crossfade below, exactly like the banner's. Its squeeze ride is the
|
||||
rule above — see hero-seed-ride.) */
|
||||
|
||||
/* The sheet, ungated like today's calc — it must track mid-scroll
|
||||
even under main-thread stall. The static transform is the REST
|
||||
@@ -5846,6 +5902,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* hero-squeeze-ride for the layers that carry their own scale. Translate
|
||||
FIRST, so it is applied in unscaled space and the seed moves the same
|
||||
number of screen pixels as the banner rather than 1.06x that. */
|
||||
@keyframes -global-hero-seed-ride {
|
||||
from {
|
||||
transform: translateY(0) scale(1.06);
|
||||
}
|
||||
to {
|
||||
transform: translateY(calc(-0.22 * var(--ride, 0px))) scale(1.06);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ground-mono-ride {
|
||||
from {
|
||||
filter: grayscale(0) brightness(1);
|
||||
|
||||
Reference in new issue
Block a user