fix(navegacao): foco em âncora de outra página, vitrine acessível e 3D mais leve

Foco cai no destino ao chegar de outra página; âncoras ignoram ?tipo=; movimento reduzido salta em todos os cliques; cronômetro da janela ampliada; degradê no topo de Projetos e legendas do mergulho com intervalos testados; vitrine acima da barra de cookies e com 2 paradas por projeto; reel-list.css no global; 3D montado em tarefas curtas, limitado a ~30 qps e estático com WebGL por software; pôsteres da vitrine de 330 para 158 KB; SC Plus regravado; scripts sem funções acima de complexidade 10.
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 19:14:48 -03:00
1 parent 9e2d65a926
commit 204ebd5fa5
49 files changed
+2197 -606

No files matched your search

+42 -12
View File
@@ -1,5 +1,6 @@
"use client";
import Image from "next/image";
import {
type CSSProperties,
type RefObject,
@@ -47,6 +48,16 @@ const headerHeight = () => {
return headerCache.height;
};
/**
* Larguras (CSS) em que os pôsteres aparecem, para o next/image pedir o arquivo do tamanho certo (um WebP/AVIF de
* ~1080 px no notebook e de ~256 px no celular) e não o original de 1440 px: o notebook mede ~58vw (até ~1100 px nas
* telas largas; 90vw abaixo de 1024 px, com a vitrine em coluna) e o celular da moldura, 22% dele.
*/
const LAPTOP_POSTER_SIZES =
"(min-width: 1900px) 1100px, (min-width: 1024px) 58vw, 90vw";
const PHONE_POSTER_SIZES =
"(min-width: 1900px) 240px, (min-width: 1024px) 13vw, 20vw";
/** Põe o vídeo no instante `fraction` (0 a 1) sem repetir buscas enquanto a anterior ainda não terminou. */
function seekVideo(video: HTMLVideoElement | null, fraction: number) {
if (!video || video.readyState < 1 || !video.duration) return;
@@ -519,16 +530,22 @@ function ReelLayer({
inert={!isActive}
>
<BrowserFrame url={displayUrl(project)} className="ds-reel-laptop">
<div
className="ds-reel-screen"
style={
poster ? { backgroundImage: `url(${desktop.poster})` } : undefined
}
>
<div className="ds-reel-screen">
{/* o pôster é a imagem de baixo (uma só, otimizada): o vídeo, por cima, cobre-a ao carregar */}
{poster && (
<Image
src={desktop.poster}
alt=""
width={desktop.width}
height={desktop.height}
sizes={LAPTOP_POSTER_SIZES}
loading="eager"
fetchPriority="low"
/>
)}
<video
ref={videoRef}
src={attached ? getScrubSource(config.id) : undefined}
poster={poster ? desktop.poster : undefined}
muted
playsInline
preload="metadata"
@@ -541,10 +558,19 @@ function ReelLayer({
/>
</div>
</BrowserFrame>
<PhoneFrame
className="ds-reel-phone"
poster={poster ? phone.poster : undefined}
/>
<PhoneFrame className="ds-reel-phone">
{poster && (
<Image
src={phone.poster}
alt=""
width={phone.width}
height={phone.height}
sizes={PHONE_POSTER_SIZES}
loading="eager"
fetchPriority="low"
/>
)}
</PhoneFrame>
</div>
);
}
@@ -615,7 +641,11 @@ function ReelCaption({
))}
</ol>
</div>
<ProjectActions item={item} onExpand={() => onExpand(config.id)} />
<ProjectActions
item={item}
onExpand={() => onExpand(config.id)}
grouped
/>
</div>
);
}