perf(movimento): sem tela vazia, passagens em CSS e vitrine leve

Projetos sobem sobre o palco do hero e o celular tem hero de uma tela sem mergulho; passagens com animation-timeline em CSS, sem GSAP nem motion; troca da vitrine sem texto duplicado; moldura de celular só com pôster e scrub reencodado em 960 px (9,4 para 5,2 MB); 3D monta na primeira interação e para quando parado; transição entre páginas do mesmo segmento; foco em anel duplo; texto de 15 px no celular; código morto removido.
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 14:15:50 -03:00
1 parent 10f170abf4
commit 318a764190
77 files changed
+4293 -4951

No files matched your search

@@ -1,6 +1,68 @@
import { act, render } from "@testing-library/react";
import FilmGrain from "./FilmGrain";
/* IntersectionObserver controlado: o teste decide quando cada tela de projeto entra ou sai da janela. */
type Observer = {
callback: IntersectionObserverCallback;
observed: Set<Element>;
disconnect: jest.Mock;
};
let observers: Observer[] = [];
const original = global.IntersectionObserver;
beforeEach(() => {
observers = [];
global.IntersectionObserver = class {
private readonly record: Observer;
constructor(callback: IntersectionObserverCallback) {
this.record = { callback, observed: new Set(), disconnect: jest.fn() };
observers.push(this.record);
}
observe(target: Element) {
this.record.observed.add(target);
}
unobserve(target: Element) {
this.record.observed.delete(target);
}
disconnect() {
this.record.disconnect();
}
takeRecords() {
return [];
}
} as unknown as typeof IntersectionObserver;
});
afterEach(() => {
global.IntersectionObserver = original;
document.body.innerHTML = "";
});
function addScreen(className: string) {
const element = document.createElement("div");
element.className = className;
document.body.append(element);
return element;
}
/** O navegador avisa que `target` entrou (ou saiu) da janela; `height` 0 é uma tela escondida por CSS. */
function intersect(target: Element, isIntersecting: boolean, height = 600) {
act(() => {
for (const observer of observers)
if (observer.observed.has(target))
observer.callback(
[
{
target,
isIntersecting,
boundingClientRect: { height },
} as unknown as IntersectionObserverEntry,
],
{} as IntersectionObserver,
);
});
}
describe("static cinematic effects", () => {
it("renders grain without a canvas", () => {
const grain = render(<FilmGrain />);
@@ -13,62 +75,36 @@ describe("static cinematic effects", () => {
});
describe("film grain over the project screens", () => {
let screenTop = 5000;
function addScreen(className: string) {
const element = document.createElement("div");
element.className = className;
element.getBoundingClientRect = () =>
({ top: screenTop, bottom: screenTop + 600, height: 600 }) as DOMRect;
document.body.append(element);
return element;
}
function scroll(to: number) {
screenTop = to;
act(() => {
window.dispatchEvent(new Event("scroll"));
jest.advanceTimersByTime(40);
});
}
beforeEach(() => {
jest.useFakeTimers();
screenTop = 5000;
Object.defineProperty(window, "innerHeight", {
configurable: true,
value: 800,
});
});
afterEach(() => {
jest.useRealTimers();
document.body.innerHTML = "";
});
it("steps aside while a project screen is in view and comes back when it leaves", () => {
addScreen("ds-reel");
const reel = addScreen("ds-reel");
const { container } = render(<FilmGrain />);
const grain = container.firstElementChild as HTMLElement;
expect(grain).not.toHaveAttribute("data-over-projects");
scroll(300); // a vitrine entrou na janela
intersect(reel, true); // a vitrine entrou na janela
expect(grain).toHaveAttribute("data-over-projects");
scroll(-900); // passou: a cena acabou 300 px acima do topo
intersect(reel, false); // passou
expect(grain).not.toHaveAttribute("data-over-projects");
});
it("also covers the browser and phone frames", () => {
addScreen("ds-bframe");
const { container } = render(<FilmGrain />);
scroll(100);
expect(container.firstElementChild).toHaveAttribute("data-over-projects");
it("also covers the browser and phone frames and the card screens", () => {
for (const className of ["ds-bframe", "ds-phone", "ds-frame"]) {
const screen = addScreen(className);
const { container, unmount } = render(<FilmGrain />);
intersect(screen, true);
expect(container.firstElementChild).toHaveAttribute("data-over-projects");
unmount();
document.body.innerHTML = "";
}
});
it("stays on when there is no project screen on the page", () => {
const { container } = render(<FilmGrain />);
scroll(100);
expect(observers[0].observed.size).toBe(0);
expect(container.firstElementChild).not.toHaveAttribute(
"data-over-projects",
);