From 7df093370958350fce211d534c3e00a438d4324b Mon Sep 17 00:00:00 2001 From: erikgh Date: Mon, 3 Aug 2026 15:34:25 -0600 Subject: [PATCH] perf(recorrido): las seis fotos dejan de descargarse antes del primer scroll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- components/sections/PuntosInflexion.tsx | 32 +++++++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/components/sections/PuntosInflexion.tsx b/components/sections/PuntosInflexion.tsx index 4ff8e39..3d4d36c 100644 --- a/components/sections/PuntosInflexion.tsx +++ b/components/sections/PuntosInflexion.tsx @@ -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() {
{/* 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) => ( - - - + {cerca && Math.abs(i - activo) <= 1 && ( + + + + )} ))}