// Base do kit minimal: cores, fontes, curvas e medidas. Todo número visual sai daqui. // Os elementos importam só este arquivo, 'react' e 'remotion'. Nada de fora da pasta do kit. import {continueRender, delayRender} from 'remotion'; import fonte0Url from './fontes/hanken.woff2'; import fonte1Url from './fontes/splinemono.woff2'; // ---------- tela ---------- export const LARGURA = 1080, ALTURA = 1920, FPS = 30; // área segura (Reels/TikTok): o que se lê fica entre estas linhas export const SEGURA = {topo: 168, base: 1330, lado: 80}; export const LARGURA_UTIL = LARGURA - SEGURA.lado * 2; // 920 px // ---------- cor ---------- export const COR = { osso: '#F7F6F2', tinta: '#161616', cobalto: '#2B4BFF', cinza: '#B9B8B2' }; // ---------- fonte ---------- // Nome de família com o prefixo do kit, pra não colidir com outro kit no mesmo estúdio. export const FONTE = "\"MN Hanken\", system-ui, sans-serif"; export const MONO = "\"MN Spline Mono\", ui-monospace, Menlo, monospace"; const fontes = [ {nome: 'MN Hanken', url: fonte0Url, peso: '200 700', estilo: 'normal'}, {nome: 'MN Spline Mono', url: fonte1Url, peso: '400', estilo: 'normal'}, ]; if (typeof document !== 'undefined' && !(globalThis as Record).__minimal) { (globalThis as Record).__minimal = true; for (const f of fontes) { const h = delayRender('minimal: ' + f.nome); new FontFace(f.nome, `url(${f.url}) format('woff2')`, {weight: f.peso, style: f.estilo}).load() .then((ff) => {document.fonts.add(ff); continueRender(h);}) .catch(() => continueRender(h)); } } // ---------- tempo e curva ---------- export const c01 = (t: number) => Math.max(0, Math.min(1, t)); /** progresso 0..1 de f entre a e b */ export const seg = (f: number, a: number, b: number) => c01((f - a) / (b - a)); /** saída rápida e pouso longo, parecida com cubic-bezier(.2,.8,.2,1) */ export const e5 = (t: number) => 1 - Math.pow(1 - c01(t), 5); /** pouso com overshoot (passa 1.1x e volta), toda entrada usa esta */ export const ov = (t: number) => { const x = c01(t) - 1; const k = 1.5; return 1 + (k + 1) * x * x * x + k * x * x; }; export const lerp = (a: number, b: number, t: number) => a + (b - a) * t; /** uma batida da trilha do kit (90 bpm) em quadros */ export const BATIDA = 20.0; // ---------- medida de texto ---------- // Mede com canvas (só guarda no cache depois que a fonte carregou) pra encolher a linha que passaria // da largura útil. Nunca deixa palavra cortada. let canvas: HTMLCanvasElement | null = null; const cache = new Map(); export const larguraTexto = (texto: string, tamanho: number, peso = 300, trackingEm = -0.03, familia = 'MN Hanken'): number => { const chave = `${texto}|${tamanho}|${peso}|${trackingEm}|${familia}`; const c = cache.get(chave); if (c !== undefined) return c; if (typeof document === 'undefined') return texto.length * tamanho * 0.56; canvas = canvas || document.createElement('canvas'); const ctx = canvas.getContext('2d'); if (!ctx) return texto.length * tamanho * 0.56; const fonte = `${peso} ${tamanho}px "${familia}"`; ctx.font = fonte; const w = ctx.measureText(texto).width + texto.length * trackingEm * tamanho; if (document.fonts.check(fonte)) cache.set(chave, w); return w; }; /** o maior tamanho até `tamanho` em que o texto cabe em `largura` */ export const caber = (texto: string, tamanho: number, largura = LARGURA_UTIL, peso = 300, trackingEm = -0.03, familia = 'MN Hanken') => { const w = larguraTexto(texto, tamanho, peso, trackingEm, familia); return w > largura ? Math.floor(tamanho * (largura / w)) : tamanho; }; /** tira os asteriscos de destaque: "Usar *IA*" -> "Usar IA" */ export const textoLimpo = (t: string) => t.replace(/\*/g, ''); /** quebra "a *b* c" em partes {t, d}: d = destaque */ export const partes = (t: string) => t.split(/(\*[^*]+\*)/).filter(Boolean).map((s) => s.startsWith('*') ? {t: s.slice(1, -1), d: true} : {t: s, d: false}); /** valores de uma lista de chaves [[quadro, v1, v2...], ...] no quadro f, com pouso suave entre as chaves */ export const chavesN = (f: number, ks: number[][]): number[] => { if (f <= ks[0][0]) return ks[0].slice(1); for (let i = 1; i < ks.length; i++) if (f <= ks[i][0]) { const t = e5(seg(f, ks[i - 1][0], ks[i][0])); return ks[i].slice(1).map((v, j) => lerp(ks[i - 1][j + 1] ?? 0, v, t)); } return ks[ks.length - 1].slice(1); }; export const MARGEM = 120;