perf(recorrido): las seis fotos dejan de descargarse antes del primer scroll
Medido sobre el export estático: la página pedía 2.067 kB antes de que el visitante moviera un dedo, y 1.243 de ellos eran las seis fotografías del recorrido. `loading="lazy"` no salvaba nada, porque el umbral de distancia de Chrome se traga una sección que empieza por debajo de los 2.000px. Ahora la escena solo monta su fotografía cuando el escenario está a menos de pantalla y cuarto, y solo la de la escena activa y sus vecinas. La siguiente entra en el documento una escena antes de hacer falta, que da tiempo de sobra a llegar. Carga inicial: 2.067 kB → 821 kB, con cero imágenes del recorrido. El resto llega repartido según avanzas. Verificado escena por escena en el export: 2 o 3 imágenes montadas a la vez, todas cargadas. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
159b03dc5e
commit
7df0933709
1 file changed
+27
-5
@@ -80,6 +80,17 @@ function Escenificado() {
|
||||
// frame is a dozen lines, needs no measurement pass from the library, and can
|
||||
// be verified in the browser, which the black box could not.
|
||||
const bruto = useMotionValue(0);
|
||||
|
||||
// Whether the stage is close enough to be worth paying for its photographs.
|
||||
// Measured on the static export: the six of them are 1,24 MB and every one
|
||||
// was downloading before the reader had scrolled a pixel — `loading="lazy"`
|
||||
// did not save us, because Chrome's distance threshold is generous enough to
|
||||
// swallow a section that starts under 2.000px down. Two megabytes before the
|
||||
// first gesture, on a page that gets opened from a WhatsApp link on mobile
|
||||
// data.
|
||||
const [cerca, setCerca] = useState(false);
|
||||
const yaCerca = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
let pendiente = 0;
|
||||
const medir = () => {
|
||||
@@ -89,6 +100,12 @@ function Escenificado() {
|
||||
const { top, height } = el.getBoundingClientRect();
|
||||
const recorrido = height - window.innerHeight;
|
||||
bruto.set(recorrido > 0 ? Math.min(1, Math.max(0, -top / recorrido)) : 0);
|
||||
// Latch, never unset: the images are cached by then, and flipping it back
|
||||
// would tear down the stage every time the reader scrolled past.
|
||||
if (!yaCerca.current && top < window.innerHeight * 1.25) {
|
||||
yaCerca.current = true;
|
||||
setCerca(true);
|
||||
}
|
||||
};
|
||||
const alScroll = () => {
|
||||
if (!pendiente) pendiente = requestAnimationFrame(medir);
|
||||
@@ -138,8 +155,11 @@ function Escenificado() {
|
||||
<div ref={contenedor} className="relative" style={{ height: `${TOTAL * 100}vh` }}>
|
||||
<div data-escena={activo} data-tiempo={tiempo} className="sticky top-0 h-screen overflow-hidden bg-azul-profundo">
|
||||
{/* The photograph of whichever point is on screen, crossfading with
|
||||
the beat. Only the active one is opaque; the rest sit at zero, so
|
||||
the browser still holds them decoded and the swap costs nothing. */}
|
||||
the beat. Only the neighbours of the active scene are mounted:
|
||||
the next one is in the document a whole scene before it is needed,
|
||||
which is time enough to arrive, and the far ones cost nothing.
|
||||
Mounting all six was 1,24 MB spent up front for five pictures the
|
||||
reader might never reach. */}
|
||||
{items.map((item, i) => (
|
||||
<motion.div
|
||||
key={item.name}
|
||||
@@ -148,9 +168,11 @@ function Escenificado() {
|
||||
transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
|
||||
className="absolute inset-0"
|
||||
>
|
||||
<motion.div style={{ y: fotoY }} className="absolute -inset-y-24 inset-x-0">
|
||||
<FondoFoto src={FOTOS[i]} wash="" className="inset-0" />
|
||||
</motion.div>
|
||||
{cerca && Math.abs(i - activo) <= 1 && (
|
||||
<motion.div style={{ y: fotoY }} className="absolute -inset-y-24 inset-x-0">
|
||||
<FondoFoto src={FOTOS[i]} wash="" className="inset-0" />
|
||||
</motion.div>
|
||||
)}
|
||||
</motion.div>
|
||||
))}
|
||||
|
||||
|
||||
Reference in new issue
Block a user