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.
75 lines
2.8 KiB
TypeScript
75 lines
2.8 KiB
TypeScript
/**
|
|
* Leituras do aparelho que decidem o que a página carrega e anima: ponteiro de toque, pouca CPU ou memória,
|
|
* "economizar dados" e largura de celular. Tudo roda só no navegador (no servidor, devolve o valor mais leve).
|
|
*/
|
|
|
|
/** Abaixo disto (px) a página usa as versões de celular dos vídeos e capas. */
|
|
export const MOBILE_MAX_WIDTH = 767;
|
|
export const MOBILE_QUERY = `(max-width: ${MOBILE_MAX_WIDTH}px)`;
|
|
|
|
type NavigatorHints = Navigator & {
|
|
deviceMemory?: number;
|
|
connection?: { saveData?: boolean };
|
|
};
|
|
|
|
function matches(query: string) {
|
|
return (
|
|
typeof window !== "undefined" &&
|
|
typeof window.matchMedia === "function" &&
|
|
window.matchMedia(query).matches
|
|
);
|
|
}
|
|
|
|
/** Ponteiro principal impreciso (dedo): sem rolagem suavizada nem cena 3D. */
|
|
export const isCoarsePointer = () => matches("(pointer: coarse)");
|
|
|
|
export const prefersReducedMotion = () =>
|
|
matches("(prefers-reduced-motion: reduce)");
|
|
|
|
/** Tela de celular: abaixo de 768 px. */
|
|
export const isMobileViewport = () => matches(MOBILE_QUERY);
|
|
|
|
/** "Economizar dados" ligado: só imagens, nenhum vídeo é baixado sozinho. */
|
|
export function isSaveData() {
|
|
if (typeof navigator === "undefined") return false;
|
|
return (navigator as NavigatorHints).connection?.saveData === true;
|
|
}
|
|
|
|
/**
|
|
* Aparelho que não deve montar WebGL: toque, 4 núcleos ou menos, 4 GB de memória ou menos, economia de dados
|
|
* ou movimento reduzido. O que não é informado pelo navegador (Safari, Firefox) não conta contra o aparelho.
|
|
*/
|
|
export function isLowPowerDevice() {
|
|
if (typeof navigator === "undefined") return true;
|
|
const hints = navigator as NavigatorHints;
|
|
return (
|
|
isCoarsePointer() ||
|
|
prefersReducedMotion() ||
|
|
isSaveData() ||
|
|
(typeof hints.hardwareConcurrency === "number" &&
|
|
hints.hardwareConcurrency <= 4) ||
|
|
(typeof hints.deviceMemory === "number" && hints.deviceMemory <= 4)
|
|
);
|
|
}
|
|
|
|
/** Nomes que os navegadores dão a um renderizador de WebGL que roda na CPU: SwiftShader (Chrome), llvmpipe (Mesa) e o Microsoft Basic Render Driver. */
|
|
const SOFTWARE_RENDERER = /swiftshader|llvmpipe|basic render/i;
|
|
|
|
/** O nome do renderizador (`UNMASKED_RENDERER_WEBGL`) é de um desenho por software? */
|
|
export const isSoftwareRenderer = (name: string) =>
|
|
SOFTWARE_RENDERER.test(name);
|
|
|
|
/**
|
|
* O contexto WebGL desenha na CPU (SwiftShader, llvmpipe, Basic Render)? Abre normalmente, mas a cena 3D sairia a
|
|
* poucos quadros por segundo. Sem a extensão `WEBGL_debug_renderer_info` (Firefox, Safari) não dá para saber, e o
|
|
* aparelho não conta contra si.
|
|
*/
|
|
export function isSoftwareWebGL(
|
|
context: WebGLRenderingContext | WebGL2RenderingContext,
|
|
) {
|
|
const info = context.getExtension("WEBGL_debug_renderer_info");
|
|
if (!info) return false;
|
|
const renderer: unknown = context.getParameter(info.UNMASKED_RENDERER_WEBGL);
|
|
return typeof renderer === "string" && isSoftwareRenderer(renderer);
|
|
}
|