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:
1 parent
f8545cd442
commit
6ac87824f3
68 files changed
+5735
-3300
No files matched your search
@@ -0,0 +1,141 @@
|
||||
import { startFramePulse } from "./FramePulse";
|
||||
|
||||
const invalidate = jest.fn();
|
||||
let stops: Array<() => void> = [];
|
||||
|
||||
/** Liga a batida e guarda o desligamento para o afterEach não deixar ouvinte para trás. */
|
||||
function start(options?: Parameters<typeof startFramePulse>[1]) {
|
||||
const stop = startFramePulse(invalidate, options);
|
||||
stops.push(stop);
|
||||
return stop;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
invalidate.mockClear();
|
||||
stops = [];
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
for (const stop of stops) stop();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
const advance = (ms: number) => jest.advanceTimersByTime(ms);
|
||||
|
||||
describe("startFramePulse", () => {
|
||||
it("asks for frames at about 30 per second, not 60", () => {
|
||||
start();
|
||||
advance(1000);
|
||||
expect(invalidate.mock.calls.length).toBeGreaterThan(20);
|
||||
expect(invalidate.mock.calls.length).toBeLessThanOrEqual(34);
|
||||
});
|
||||
|
||||
it("asks for the first frame right away", () => {
|
||||
start();
|
||||
expect(invalidate).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("is a single setInterval, never a second animation-frame loop", () => {
|
||||
const frames = jest.spyOn(window, "requestAnimationFrame");
|
||||
start();
|
||||
expect(jest.getTimerCount()).toBe(1);
|
||||
advance(1000);
|
||||
expect(frames).not.toHaveBeenCalled();
|
||||
frames.mockRestore();
|
||||
});
|
||||
|
||||
it("does nothing once it is stopped (the hero is off screen)", () => {
|
||||
const stop = start();
|
||||
advance(200);
|
||||
const running = invalidate.mock.calls.length;
|
||||
expect(running).toBeGreaterThan(0);
|
||||
|
||||
stop();
|
||||
advance(1000);
|
||||
expect(invalidate.mock.calls.length).toBe(running);
|
||||
expect(jest.getTimerCount()).toBe(0);
|
||||
});
|
||||
|
||||
it("removes its listeners when it stops", () => {
|
||||
const stop = start();
|
||||
stop();
|
||||
invalidate.mockClear();
|
||||
|
||||
window.dispatchEvent(new Event("pointermove"));
|
||||
advance(500);
|
||||
expect(invalidate).not.toHaveBeenCalled();
|
||||
expect(jest.getTimerCount()).toBe(0);
|
||||
});
|
||||
|
||||
it("can be stopped twice", () => {
|
||||
const stop = start();
|
||||
stop();
|
||||
expect(() => stop()).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("startFramePulse: stops when nobody is looking", () => {
|
||||
it("stops drawing, and has no timer left, after 5 s without a cursor or scroll", () => {
|
||||
start();
|
||||
advance(4000);
|
||||
const drawn = invalidate.mock.calls.length;
|
||||
expect(drawn).toBeGreaterThan(80); // ainda batendo, ~30 por segundo
|
||||
|
||||
advance(1500); // passou dos 5 s
|
||||
const stopped = invalidate.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(invalidate.mock.calls.length).toBe(stopped);
|
||||
expect(jest.getTimerCount()).toBe(0);
|
||||
});
|
||||
|
||||
it.each([["pointermove"], ["scroll"], ["wheel"], ["keydown"]])(
|
||||
"wakes up again with %s and keeps drawing",
|
||||
(type) => {
|
||||
start();
|
||||
advance(7000);
|
||||
const stopped = invalidate.mock.calls.length;
|
||||
|
||||
window.dispatchEvent(new Event(type));
|
||||
advance(500);
|
||||
|
||||
expect(invalidate.mock.calls.length).toBeGreaterThan(stopped + 8);
|
||||
},
|
||||
);
|
||||
|
||||
it("wakes up once, not once per gesture (one timer, however many events arrive)", () => {
|
||||
start();
|
||||
for (let i = 0; i < 20; i++) window.dispatchEvent(new Event("pointermove"));
|
||||
expect(jest.getTimerCount()).toBe(1);
|
||||
});
|
||||
|
||||
it("each gesture buys another 5 s: it does not stop while there is movement", () => {
|
||||
start();
|
||||
for (let i = 0; i < 4; i++) {
|
||||
advance(3000);
|
||||
window.dispatchEvent(new Event("pointermove"));
|
||||
}
|
||||
const before = invalidate.mock.calls.length;
|
||||
|
||||
advance(1000);
|
||||
|
||||
expect(invalidate.mock.calls.length).toBeGreaterThan(before + 20);
|
||||
});
|
||||
|
||||
it("lets the idle time and the rate be configured", () => {
|
||||
start({ idleMs: 1000 });
|
||||
advance(1500);
|
||||
const stopped = invalidate.mock.calls.length;
|
||||
advance(2000);
|
||||
expect(invalidate.mock.calls.length).toBe(stopped);
|
||||
|
||||
invalidate.mockClear();
|
||||
start({ fps: 10 });
|
||||
advance(1000);
|
||||
expect(invalidate.mock.calls.length).toBeLessThanOrEqual(11);
|
||||
expect(invalidate.mock.calls.length).toBeGreaterThanOrEqual(9);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user