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:
1 parent
10f170abf4
commit
318a764190
77 files changed
+4293
-4951
No files matched your search
@@ -17,34 +17,110 @@ afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
const advance = (ms: number) =>
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(ms);
|
||||
});
|
||||
|
||||
describe("FramePulse", () => {
|
||||
it("asks for frames at about 30 per second while active, not 60", () => {
|
||||
render(<FramePulse active />);
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(1000);
|
||||
});
|
||||
advance(1000);
|
||||
expect(mockInvalidate.mock.calls.length).toBeGreaterThan(20);
|
||||
expect(mockInvalidate.mock.calls.length).toBeLessThanOrEqual(34);
|
||||
});
|
||||
|
||||
it("draws nothing at all when it is not active (off screen)", () => {
|
||||
const { rerender } = render(<FramePulse active={false} />);
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(1000);
|
||||
});
|
||||
advance(1000);
|
||||
expect(mockInvalidate).not.toHaveBeenCalled();
|
||||
|
||||
rerender(<FramePulse active />);
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(200);
|
||||
});
|
||||
advance(200);
|
||||
const running = mockInvalidate.mock.calls.length;
|
||||
expect(running).toBeGreaterThan(0);
|
||||
|
||||
rerender(<FramePulse active={false} />);
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(1000);
|
||||
});
|
||||
advance(1000);
|
||||
expect(mockInvalidate.mock.calls.length).toBe(running);
|
||||
});
|
||||
});
|
||||
|
||||
describe("FramePulse: stops when nobody is looking", () => {
|
||||
it("stops drawing, and stops asking for animation frames, after 5 s without a cursor or scroll", () => {
|
||||
render(<FramePulse active />);
|
||||
advance(4000);
|
||||
const drawn = mockInvalidate.mock.calls.length;
|
||||
expect(drawn).toBeGreaterThan(80); // ainda batendo, ~30 por segundo
|
||||
|
||||
advance(1500); // passou dos 5 s
|
||||
const stopped = mockInvalidate.mock.calls.length;
|
||||
// parou perto dos 5 s: no máximo 5 s de batidas a ~30 por segundo
|
||||
expect(stopped).toBeLessThanOrEqual(5 * 32);
|
||||
|
||||
advance(3000);
|
||||
expect(mockInvalidate.mock.calls.length).toBe(stopped);
|
||||
// sem laço nenhum: nenhum quadro de animação pendente
|
||||
expect(jest.getTimerCount()).toBe(0);
|
||||
});
|
||||
|
||||
it.each([["pointermove"], ["scroll"], ["wheel"], ["keydown"]])(
|
||||
"wakes up again with %s and keeps drawing",
|
||||
(type) => {
|
||||
render(<FramePulse active />);
|
||||
advance(7000);
|
||||
const stopped = mockInvalidate.mock.calls.length;
|
||||
|
||||
act(() => {
|
||||
window.dispatchEvent(new Event(type));
|
||||
});
|
||||
advance(500);
|
||||
|
||||
expect(mockInvalidate.mock.calls.length).toBeGreaterThan(stopped + 8);
|
||||
},
|
||||
);
|
||||
|
||||
it("each gesture buys another 5 s: it does not stop while there is movement", () => {
|
||||
render(<FramePulse active />);
|
||||
for (let i = 0; i < 4; i++) {
|
||||
advance(3000);
|
||||
act(() => {
|
||||
window.dispatchEvent(new Event("pointermove"));
|
||||
});
|
||||
}
|
||||
const before = mockInvalidate.mock.calls.length;
|
||||
|
||||
advance(1000);
|
||||
|
||||
expect(mockInvalidate.mock.calls.length).toBeGreaterThan(before + 20);
|
||||
});
|
||||
|
||||
it("lets the idle time be configured", () => {
|
||||
render(<FramePulse active idleMs={1000} />);
|
||||
advance(1500);
|
||||
const stopped = mockInvalidate.mock.calls.length;
|
||||
|
||||
advance(2000);
|
||||
|
||||
expect(mockInvalidate.mock.calls.length).toBe(stopped);
|
||||
});
|
||||
|
||||
it("removes its listeners when it unmounts or goes inactive", () => {
|
||||
const { rerender, unmount } = render(<FramePulse active />);
|
||||
rerender(<FramePulse active={false} />);
|
||||
mockInvalidate.mockClear();
|
||||
|
||||
act(() => {
|
||||
window.dispatchEvent(new Event("pointermove"));
|
||||
});
|
||||
advance(500);
|
||||
expect(mockInvalidate).not.toHaveBeenCalled();
|
||||
|
||||
unmount();
|
||||
act(() => {
|
||||
window.dispatchEvent(new Event("scroll"));
|
||||
});
|
||||
advance(500);
|
||||
expect(mockInvalidate).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user