perf(movimento): sem tempo morto, vídeos regravados e 3D sem React Three Fiber

Legenda do mergulho até Projetos cobrir a cena; transição de página sem blur e com fundo escuro; foco no conteúdo ao trocar de página; listas com marcador e dois pesos de título; vídeos da SEV e da SC Plus regravados sem quadros brancos; scrub a 12-15 fps (-30%); vitrine com uma parada de Tab e setas, contador no celular e CSS sob demanda; âncoras num módulo só (fragment-nav); FramePulse com setInterval; CSS morto removido.
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 17:01:37 -03:00
1 parent f8545cd442
commit 6ac87824f3
68 files changed
+5735 -3300

No files matched your search

+24 -11
View File
@@ -1,13 +1,16 @@
/**
* Recodifica um vídeo para a vitrine animada, em que o tempo do vídeo muda a cada quadro da rolagem.
* Para a busca ser instantânea o vídeo precisa de um quadro-chave a cada 6 quadros e nenhum B-frame; o tamanho
* de cada arquivo fica em até 3 MB (sobe o CRF e, se preciso, reduz a imagem até caber).
* Para a busca ser instantânea o vídeo precisa de um quadro-chave a cada 6 quadros e nenhum B-frame. O vídeo roda
* a 12 quadros por segundo (--fps): o tempo dele anda devagar em relação à rolagem (cerca de 1 s de vídeo por 50 px),
* então 12 quadros por segundo de vídeo são dezenas por segundo de tela, e o arquivo cai a menos da metade. O tamanho
* de cada arquivo fica em até 1,2 MB (--max-mb): sobe o CRF (34, 37, 40, 43) e, se preciso, reduz a imagem até caber.
*
* ffmpeg-static não é dependência do site: instale-o numa pasta temporária e rode o script a partir dela
* (as dependências são resolvidas a partir da pasta onde o comando foi executado):
* (as dependências são resolvidas a partir da pasta onde o comando foi executado; vale também um `ffmpeg` no PATH
* ou --ffmpeg <exe>):
*
* mkdir rec && cd rec && npm init -y && npm i ffmpeg-static
* node <repo>/scripts/encode-scrub-video.mjs --in desktop-original.mp4 --out <repo>/public/videos/<id>/scrub-desktop.mp4 --size 1120x700
* node <repo>/scripts/encode-scrub-video.mjs --in desktop-original.mp4 --out <repo>/public/videos/<id>/scrub-desktop.mp4 --size 960x600
*
* Só existe o scrub do computador: o celular da moldura mostra o pôster, sem vídeo.
*
@@ -23,21 +26,31 @@ import { createRequire } from "node:module";
import { join } from "node:path";
const require = createRequire(join(process.cwd(), "noop.js"));
const ffmpegPath = require("ffmpeg-static");
const args = Object.fromEntries(
process.argv.slice(2).reduce((acc, cur, i, all) => {
if (cur.startsWith("--")) acc.push([cur.slice(2), all[i + 1]]);
return acc;
}, []),
);
function resolveFfmpeg() {
if (args.ffmpeg) return args.ffmpeg;
try {
return require("ffmpeg-static");
} catch {
return "ffmpeg";
}
}
const ffmpegPath = resolveFfmpeg();
const { in: input, out: output } = args;
const [width, height] = (args.size ?? "1120x700").split("x").map(Number);
const MAX_BYTES = Number(args["max-mb"] ?? 3) * 1024 * 1024;
const [width, height] = (args.size ?? "960x600").split("x").map(Number);
const FPS = Number(args.fps ?? 12);
const MAX_BYTES = Number(args["max-mb"] ?? 1.2) * 1024 * 1024;
const CRFS = (args.crfs ?? "34,37,40,43").split(",").map(Number);
if (!input || !output || !width || !height) {
console.error(
"uso: --in <video> --out <video.mp4> --size 1120x700 [--max-mb 3]",
"uso: --in <video> --out <video.mp4> [--size 960x600] [--fps 12] [--max-mb 1.2] [--crfs 34,37,40,43] [--ffmpeg <exe>]",
);
process.exit(1);
}
@@ -53,7 +66,7 @@ function encode(w, h, crf) {
"-i",
input,
"-vf",
`scale=${w}:${h}:flags=lanczos,fps=24`,
`scale=${w}:${h}:flags=lanczos,fps=${FPS}`,
"-an",
"-c:v",
"libx264",
@@ -88,7 +101,7 @@ function encode(w, h, crf) {
for (const scale of [1, 0.9, 0.8]) {
const w = Math.round((width * scale) / 2) * 2;
const h = Math.round((height * scale) / 2) * 2;
for (const crf of [28, 31, 34, 37]) {
for (const crf of CRFS) {
const bytes = encode(w, h, crf);
if (bytes <= MAX_BYTES) {
console.log(