/** * 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); }